michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsSMILInstanceTime.h" michael@0: #include "nsSMILInterval.h" michael@0: #include "nsSMILTimeValueSpec.h" michael@0: #include "mozilla/AutoRestore.h" michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation michael@0: michael@0: nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime, michael@0: nsSMILInstanceTimeSource aSource, michael@0: nsSMILTimeValueSpec* aCreator, michael@0: nsSMILInterval* aBaseInterval) michael@0: : mTime(aTime), michael@0: mFlags(0), michael@0: mVisited(false), michael@0: mFixedEndpointRefCnt(0), michael@0: mSerial(0), michael@0: mCreator(aCreator), michael@0: mBaseInterval(nullptr) // This will get set to aBaseInterval in a call to michael@0: // SetBaseInterval() at end of constructor michael@0: { michael@0: switch (aSource) { michael@0: case SOURCE_NONE: michael@0: // No special flags michael@0: break; michael@0: michael@0: case SOURCE_DOM: michael@0: mFlags = kDynamic | kFromDOM; michael@0: break; michael@0: michael@0: case SOURCE_SYNCBASE: michael@0: mFlags = kMayUpdate; michael@0: break; michael@0: michael@0: case SOURCE_EVENT: michael@0: mFlags = kDynamic; michael@0: break; michael@0: } michael@0: michael@0: SetBaseInterval(aBaseInterval); michael@0: } michael@0: michael@0: nsSMILInstanceTime::~nsSMILInstanceTime() michael@0: { michael@0: NS_ABORT_IF_FALSE(!mBaseInterval, michael@0: "Destroying instance time without first calling Unlink()"); michael@0: NS_ABORT_IF_FALSE(mFixedEndpointRefCnt == 0, michael@0: "Destroying instance time that is still used as the fixed endpoint of an " michael@0: "interval"); michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::Unlink() michael@0: { michael@0: nsRefPtr deathGrip(this); michael@0: if (mBaseInterval) { michael@0: mBaseInterval->RemoveDependentTime(*this); michael@0: mBaseInterval = nullptr; michael@0: } michael@0: mCreator = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::HandleChangedInterval( michael@0: const nsSMILTimeContainer* aSrcContainer, michael@0: bool aBeginObjectChanged, michael@0: bool aEndObjectChanged) michael@0: { michael@0: // It's possible a sequence of notifications might cause our base interval to michael@0: // be updated and then deleted. Furthermore, the delete might happen whilst michael@0: // we're still in the queue to be notified of the change. In any case, if we michael@0: // don't have a base interval, just ignore the change. michael@0: if (!mBaseInterval) michael@0: return; michael@0: michael@0: NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not."); michael@0: michael@0: if (mVisited) { michael@0: // Break the cycle here michael@0: Unlink(); michael@0: return; michael@0: } michael@0: michael@0: bool objectChanged = mCreator->DependsOnBegin() ? aBeginObjectChanged : michael@0: aEndObjectChanged; michael@0: michael@0: mozilla::AutoRestore setVisited(mVisited); michael@0: mVisited = true; michael@0: michael@0: nsRefPtr deathGrip(this); michael@0: mCreator->HandleChangedInstanceTime(*GetBaseTime(), aSrcContainer, *this, michael@0: objectChanged); michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::HandleDeletedInterval() michael@0: { michael@0: NS_ABORT_IF_FALSE(mBaseInterval, michael@0: "Got call to HandleDeletedInterval on an independent instance time"); michael@0: NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not"); michael@0: michael@0: mBaseInterval = nullptr; michael@0: mFlags &= ~kMayUpdate; // Can't update without a base interval michael@0: michael@0: nsRefPtr deathGrip(this); michael@0: mCreator->HandleDeletedInstanceTime(*this); michael@0: mCreator = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::HandleFilteredInterval() michael@0: { michael@0: NS_ABORT_IF_FALSE(mBaseInterval, michael@0: "Got call to HandleFilteredInterval on an independent instance time"); michael@0: michael@0: mBaseInterval = nullptr; michael@0: mFlags &= ~kMayUpdate; // Can't update without a base interval michael@0: mCreator = nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsSMILInstanceTime::ShouldPreserve() const michael@0: { michael@0: return mFixedEndpointRefCnt > 0 || (mFlags & kWasDynamicEndpoint); michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::UnmarkShouldPreserve() michael@0: { michael@0: mFlags &= ~kWasDynamicEndpoint; michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::AddRefFixedEndpoint() michael@0: { michael@0: NS_ABORT_IF_FALSE(mFixedEndpointRefCnt < UINT16_MAX, michael@0: "Fixed endpoint reference count upper limit reached"); michael@0: ++mFixedEndpointRefCnt; michael@0: mFlags &= ~kMayUpdate; // Once fixed, always fixed michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::ReleaseFixedEndpoint() michael@0: { michael@0: NS_ABORT_IF_FALSE(mFixedEndpointRefCnt > 0, "Duplicate release"); michael@0: --mFixedEndpointRefCnt; michael@0: if (mFixedEndpointRefCnt == 0 && IsDynamic()) { michael@0: mFlags |= kWasDynamicEndpoint; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsSMILInstanceTime::IsDependentOn(const nsSMILInstanceTime& aOther) const michael@0: { michael@0: if (mVisited) michael@0: return false; michael@0: michael@0: const nsSMILInstanceTime* myBaseTime = GetBaseTime(); michael@0: if (!myBaseTime) michael@0: return false; michael@0: michael@0: if (myBaseTime == &aOther) michael@0: return true; michael@0: michael@0: mozilla::AutoRestore setVisited(mVisited); michael@0: mVisited = true; michael@0: return myBaseTime->IsDependentOn(aOther); michael@0: } michael@0: michael@0: const nsSMILInstanceTime* michael@0: nsSMILInstanceTime::GetBaseTime() const michael@0: { michael@0: if (!mBaseInterval) { michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(mCreator, "Base interval is set but there is no creator."); michael@0: if (!mCreator) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return mCreator->DependsOnBegin() ? mBaseInterval->Begin() : michael@0: mBaseInterval->End(); michael@0: } michael@0: michael@0: void michael@0: nsSMILInstanceTime::SetBaseInterval(nsSMILInterval* aBaseInterval) michael@0: { michael@0: NS_ABORT_IF_FALSE(!mBaseInterval, michael@0: "Attempting to reassociate an instance time with a different interval."); michael@0: michael@0: if (aBaseInterval) { michael@0: NS_ABORT_IF_FALSE(mCreator, michael@0: "Attempting to create a dependent instance time without reference " michael@0: "to the creating nsSMILTimeValueSpec object."); michael@0: if (!mCreator) michael@0: return; michael@0: michael@0: aBaseInterval->AddDependentTime(*this); michael@0: } michael@0: michael@0: mBaseInterval = aBaseInterval; michael@0: }