2009-01-14 20:38:07 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is the Mozilla SMIL module.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Brian Birtles.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2005
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Brian Birtles <birtles@gmail.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsSMILTimeValueSpec.h"
|
2010-01-12 12:00:49 -08:00
|
|
|
#include "nsSMILInterval.h"
|
|
|
|
#include "nsSMILTimeContainer.h"
|
2009-01-14 20:38:07 -08:00
|
|
|
#include "nsSMILTimeValue.h"
|
|
|
|
#include "nsSMILTimedElement.h"
|
|
|
|
#include "nsSMILInstanceTime.h"
|
|
|
|
#include "nsSMILParserUtils.h"
|
2010-01-12 12:00:49 -08:00
|
|
|
#include "nsISMILAnimationElement.h"
|
|
|
|
#include "nsContentUtils.h"
|
2009-01-14 20:38:07 -08:00
|
|
|
#include "nsString.h"
|
|
|
|
|
2010-01-12 12:00:49 -08:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Implementation
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
// Disable "warning C4355: 'this' : used in base member initializer list".
|
|
|
|
// We can ignore that warning because we know that mTimebase's constructor
|
|
|
|
// doesn't dereference the pointer passed to it.
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable:4355)
|
|
|
|
#endif
|
|
|
|
nsSMILTimeValueSpec::nsSMILTimeValueSpec(nsSMILTimedElement& aOwner,
|
2009-01-14 20:38:07 -08:00
|
|
|
PRBool aIsBegin)
|
2010-01-12 12:00:49 -08:00
|
|
|
: mOwner(&aOwner),
|
2009-01-14 20:38:07 -08:00
|
|
|
mIsBegin(aIsBegin),
|
2010-01-12 12:00:49 -08:00
|
|
|
mTimebase(this)
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
2009-01-14 20:38:07 -08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-01-12 12:00:49 -08:00
|
|
|
nsSMILTimeValueSpec::~nsSMILTimeValueSpec()
|
|
|
|
{
|
|
|
|
UnregisterFromTimebase(GetTimebaseElement());
|
|
|
|
}
|
2009-01-14 20:38:07 -08:00
|
|
|
|
|
|
|
nsresult
|
2010-01-12 12:00:49 -08:00
|
|
|
nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
|
|
|
|
nsIContent* aContextNode)
|
2009-01-14 20:38:07 -08:00
|
|
|
{
|
2010-01-12 12:00:49 -08:00
|
|
|
nsSMILTimeValueSpecParams params;
|
|
|
|
nsresult rv =
|
|
|
|
nsSMILParserUtils::ParseTimeValueSpecParams(aStringSpec, params);
|
|
|
|
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2009-01-14 20:38:07 -08:00
|
|
|
|
2010-01-12 12:00:49 -08:00
|
|
|
mParams = params;
|
2009-07-15 11:33:31 -07:00
|
|
|
|
2010-01-12 12:00:49 -08:00
|
|
|
// According to SMIL 3.0:
|
|
|
|
// The special value "indefinite" does not yield an instance time in the
|
|
|
|
// begin list. It will, however yield a single instance with the value
|
|
|
|
// "indefinite" in an end list. This value is not removed by a reset.
|
|
|
|
if (mParams.mType == nsSMILTimeValueSpecParams::OFFSET ||
|
|
|
|
(!mIsBegin && mParams.mType == nsSMILTimeValueSpecParams::INDEFINITE)) {
|
|
|
|
nsRefPtr<nsSMILInstanceTime> instance =
|
2010-03-01 11:31:50 -08:00
|
|
|
new nsSMILInstanceTime(mParams.mOffset);
|
2010-01-12 12:00:49 -08:00
|
|
|
if (!instance)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
2009-01-14 20:38:07 -08:00
|
|
|
mOwner->AddInstanceTime(instance, mIsBegin);
|
|
|
|
}
|
|
|
|
|
2010-01-12 12:00:49 -08:00
|
|
|
ResolveReferences(aContextNode);
|
|
|
|
|
2009-01-14 20:38:07 -08:00
|
|
|
return rv;
|
|
|
|
}
|
2010-01-12 12:00:49 -08:00
|
|
|
|
|
|
|
void
|
|
|
|
nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
|
|
|
|
{
|
|
|
|
if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(aContextNode,
|
|
|
|
"null context node for resolving timing references against");
|
|
|
|
|
|
|
|
// If we're not bound to the document yet, don't worry, we'll get called again
|
|
|
|
// when that happens
|
|
|
|
if (!aContextNode->IsInDoc())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Hold ref to the old content so that it isn't destroyed in between resetting
|
|
|
|
// the timebase and using the pointer to update the timebase.
|
|
|
|
nsRefPtr<nsIContent> oldTimebaseContent = mTimebase.get();
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(mParams.mDependentElemID, "NULL syncbase element id");
|
|
|
|
nsString idStr;
|
|
|
|
mParams.mDependentElemID->ToString(idStr);
|
|
|
|
mTimebase.ResetWithID(aContextNode, idStr);
|
|
|
|
UpdateTimebase(oldTimebaseContent, mTimebase.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-03-01 11:31:50 -08:00
|
|
|
nsSMILTimeValueSpec::HandleNewInterval(nsSMILInterval& aInterval,
|
2010-01-12 12:00:49 -08:00
|
|
|
const nsSMILTimeContainer* aSrcContainer)
|
|
|
|
{
|
|
|
|
const nsSMILInstanceTime& baseInstance = mParams.mSyncBegin
|
|
|
|
? *aInterval.Begin() : *aInterval.End();
|
|
|
|
nsSMILTimeValue newTime =
|
|
|
|
ConvertBetweenTimeContainers(baseInstance.Time(), aSrcContainer);
|
|
|
|
|
|
|
|
// Apply offset
|
|
|
|
if (newTime.IsResolved()) {
|
|
|
|
newTime.SetMillis(newTime.GetMillis() + mParams.mOffset.GetMillis());
|
|
|
|
}
|
|
|
|
|
2010-03-01 11:31:50 -08:00
|
|
|
// Create the instance time and register it with the interval
|
2010-01-12 12:00:49 -08:00
|
|
|
nsRefPtr<nsSMILInstanceTime> newInstance =
|
2010-03-01 11:31:50 -08:00
|
|
|
new nsSMILInstanceTime(newTime, nsSMILInstanceTime::SOURCE_SYNCBASE, this,
|
|
|
|
&aInterval);
|
2010-01-12 12:00:49 -08:00
|
|
|
if (!newInstance)
|
|
|
|
return;
|
|
|
|
|
2010-03-01 11:31:50 -08:00
|
|
|
// If we are a begin spec but the time we've got is not resolved, we won't add
|
|
|
|
// it to the owner just yet. When the time later becomes resolved we'll add it
|
|
|
|
// at that point.
|
|
|
|
if (mIsBegin && !newTime.IsResolved())
|
|
|
|
return;
|
2010-01-12 12:00:49 -08:00
|
|
|
|
|
|
|
mOwner->AddInstanceTime(newInstance, mIsBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-03-01 11:31:50 -08:00
|
|
|
nsSMILTimeValueSpec::HandleChangedInstanceTime(
|
|
|
|
const nsSMILInstanceTime& aBaseTime,
|
|
|
|
const nsSMILTimeContainer* aSrcContainer,
|
|
|
|
nsSMILInstanceTime& aInstanceTimeToUpdate,
|
|
|
|
PRBool aObjectChanged)
|
2010-01-12 12:00:49 -08:00
|
|
|
{
|
2010-03-01 11:31:50 -08:00
|
|
|
// If the instance time is fixed (e.g. because it's being used as the begin
|
|
|
|
// time of an active interval) we just ignore the change.
|
|
|
|
if (!aInstanceTimeToUpdate.MayUpdate())
|
2010-01-12 12:00:49 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
nsSMILTimeValue updatedTime =
|
2010-03-01 11:31:50 -08:00
|
|
|
ConvertBetweenTimeContainers(aBaseTime.Time(), aSrcContainer);
|
2010-01-12 12:00:49 -08:00
|
|
|
|
|
|
|
// Apply offset
|
|
|
|
if (updatedTime.IsResolved()) {
|
|
|
|
updatedTime.SetMillis(updatedTime.GetMillis() +
|
|
|
|
mParams.mOffset.GetMillis());
|
|
|
|
}
|
|
|
|
|
2010-03-01 11:31:50 -08:00
|
|
|
// Since we never add unresolved begin times to the owner we must detect if
|
|
|
|
// this change requires adding a newly-resolved time, removing
|
|
|
|
// a previously-resolved time, or doing nothing
|
|
|
|
if (mIsBegin) {
|
|
|
|
// Add newly-resolved time
|
|
|
|
if (!aInstanceTimeToUpdate.Time().IsResolved() &&
|
|
|
|
updatedTime.IsResolved()) {
|
|
|
|
aInstanceTimeToUpdate.DependentUpdate(updatedTime);
|
|
|
|
mOwner->AddInstanceTime(&aInstanceTimeToUpdate, mIsBegin);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Remove previously-resolved time
|
|
|
|
if (aInstanceTimeToUpdate.Time().IsResolved() &&
|
|
|
|
!updatedTime.IsResolved()) {
|
|
|
|
aInstanceTimeToUpdate.DependentUpdate(updatedTime);
|
|
|
|
mOwner->RemoveInstanceTime(&aInstanceTimeToUpdate, mIsBegin);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Do nothing (but update in case we're updating an 'unresolved' time to an
|
|
|
|
// 'indefinite' time or vice versa, both of which return PR_FALSE for
|
|
|
|
// IsResolved() and neither of which should be added to the owner).
|
|
|
|
if (!aInstanceTimeToUpdate.Time().IsResolved() &&
|
|
|
|
!updatedTime.IsResolved()) {
|
|
|
|
aInstanceTimeToUpdate.DependentUpdate(updatedTime);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2010-01-12 12:00:49 -08:00
|
|
|
|
|
|
|
// The timed element that owns the instance time does the updating so it can
|
|
|
|
// re-sort its array of instance times more efficiently
|
2010-03-01 11:31:50 -08:00
|
|
|
if (aInstanceTimeToUpdate.Time() != updatedTime || aObjectChanged) {
|
|
|
|
mOwner->UpdateInstanceTime(&aInstanceTimeToUpdate, updatedTime, mIsBegin);
|
2010-01-12 12:00:49 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-03-01 11:31:50 -08:00
|
|
|
nsSMILTimeValueSpec::HandleDeletedInstanceTime(
|
|
|
|
nsSMILInstanceTime &aInstanceTime)
|
2010-01-12 12:00:49 -08:00
|
|
|
{
|
2010-03-01 11:31:50 -08:00
|
|
|
// If it's an unresolved begin time then we won't have added it
|
|
|
|
if (mIsBegin && !aInstanceTime.Time().IsResolved())
|
2010-01-12 12:00:49 -08:00
|
|
|
return;
|
|
|
|
|
2010-03-01 11:31:50 -08:00
|
|
|
mOwner->RemoveInstanceTime(&aInstanceTime, mIsBegin);
|
|
|
|
}
|
2010-01-12 12:00:49 -08:00
|
|
|
|
2010-03-01 11:31:50 -08:00
|
|
|
PRBool
|
|
|
|
nsSMILTimeValueSpec::DependsOnBegin() const
|
|
|
|
{
|
|
|
|
return mParams.mSyncBegin;
|
2010-01-12 12:00:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsSMILTimeValueSpec::Traverse(nsCycleCollectionTraversalCallback* aCallback)
|
|
|
|
{
|
|
|
|
mTimebase.Traverse(aCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsSMILTimeValueSpec::Unlink()
|
|
|
|
{
|
|
|
|
UnregisterFromTimebase(GetTimebaseElement());
|
|
|
|
mTimebase.Unlink();
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Implementation helpers
|
|
|
|
|
|
|
|
void
|
|
|
|
nsSMILTimeValueSpec::UpdateTimebase(nsIContent* aFrom, nsIContent* aTo)
|
|
|
|
{
|
|
|
|
if (aFrom == aTo)
|
|
|
|
return;
|
|
|
|
|
|
|
|
UnregisterFromTimebase(GetTimedElementFromContent(aFrom));
|
|
|
|
|
|
|
|
nsSMILTimedElement* to = GetTimedElementFromContent(aTo);
|
|
|
|
if (to) {
|
|
|
|
to->AddDependent(*this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsSMILTimeValueSpec::UnregisterFromTimebase(nsSMILTimedElement* aTimedElement)
|
|
|
|
{
|
|
|
|
if (!aTimedElement)
|
|
|
|
return;
|
|
|
|
|
|
|
|
aTimedElement->RemoveDependent(*this);
|
2010-03-01 11:31:50 -08:00
|
|
|
mOwner->RemoveInstanceTimesForCreator(this, mIsBegin);
|
2010-01-12 12:00:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
nsSMILTimedElement*
|
|
|
|
nsSMILTimeValueSpec::GetTimedElementFromContent(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
if (!aContent)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsISMILAnimationElement> animElement = do_QueryInterface(aContent);
|
|
|
|
if (!animElement)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
return &animElement->TimedElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsSMILTimedElement*
|
|
|
|
nsSMILTimeValueSpec::GetTimebaseElement()
|
|
|
|
{
|
|
|
|
return GetTimedElementFromContent(mTimebase.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
nsSMILTimeValue
|
|
|
|
nsSMILTimeValueSpec::ConvertBetweenTimeContainers(
|
|
|
|
const nsSMILTimeValue& aSrcTime,
|
|
|
|
const nsSMILTimeContainer* aSrcContainer)
|
|
|
|
{
|
|
|
|
// If the source time is either indefinite or unresolved the result is going
|
|
|
|
// to be the same
|
|
|
|
if (!aSrcTime.IsResolved())
|
|
|
|
return aSrcTime;
|
|
|
|
|
|
|
|
// Convert from source time container to our parent time container
|
|
|
|
const nsSMILTimeContainer* dstContainer = mOwner->GetTimeContainer();
|
|
|
|
if (dstContainer == aSrcContainer)
|
|
|
|
return aSrcTime;
|
|
|
|
|
|
|
|
// If one of the elements is not attached to a time container then we can't do
|
|
|
|
// any meaningful conversion
|
|
|
|
if (!aSrcContainer || !dstContainer)
|
|
|
|
return nsSMILTimeValue(); // unresolved
|
|
|
|
|
|
|
|
nsSMILTimeValue docTime =
|
|
|
|
aSrcContainer->ContainerToParentTime(aSrcTime.GetMillis());
|
|
|
|
|
|
|
|
if (docTime.IsIndefinite())
|
|
|
|
// This will happen if the source container is paused and we have a future
|
|
|
|
// time. Just return the indefinite time.
|
|
|
|
return docTime;
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(docTime.IsResolved(),
|
|
|
|
"ContainerToParentTime gave us an unresolved time");
|
|
|
|
|
|
|
|
return dstContainer->ParentToContainerTime(docTime.GetMillis());
|
|
|
|
}
|