dom/smil/nsSMILTimeValueSpec.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/smil/nsSMILTimeValueSpec.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,534 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/EventListenerManager.h"
    1.10 +#include "mozilla/dom/SVGAnimationElement.h"
    1.11 +#include "nsSMILTimeValueSpec.h"
    1.12 +#include "nsSMILInterval.h"
    1.13 +#include "nsSMILTimeContainer.h"
    1.14 +#include "nsSMILTimeValue.h"
    1.15 +#include "nsSMILTimedElement.h"
    1.16 +#include "nsSMILInstanceTime.h"
    1.17 +#include "nsSMILParserUtils.h"
    1.18 +#include "nsIDOMKeyEvent.h"
    1.19 +#include "nsIDOMTimeEvent.h"
    1.20 +#include "nsString.h"
    1.21 +#include <limits>
    1.22 +
    1.23 +using namespace mozilla;
    1.24 +using namespace mozilla::dom;
    1.25 +
    1.26 +//----------------------------------------------------------------------
    1.27 +// Nested class: EventListener
    1.28 +
    1.29 +NS_IMPL_ISUPPORTS(nsSMILTimeValueSpec::EventListener, nsIDOMEventListener)
    1.30 +
    1.31 +NS_IMETHODIMP
    1.32 +nsSMILTimeValueSpec::EventListener::HandleEvent(nsIDOMEvent* aEvent)
    1.33 +{
    1.34 +  if (mSpec) {
    1.35 +    mSpec->HandleEvent(aEvent);
    1.36 +  }
    1.37 +  return NS_OK;
    1.38 +}
    1.39 +
    1.40 +//----------------------------------------------------------------------
    1.41 +// Implementation
    1.42 +
    1.43 +nsSMILTimeValueSpec::nsSMILTimeValueSpec(nsSMILTimedElement& aOwner,
    1.44 +                                         bool aIsBegin)
    1.45 +  : mOwner(&aOwner),
    1.46 +    mIsBegin(aIsBegin),
    1.47 +    mReferencedElement(MOZ_THIS_IN_INITIALIZER_LIST())
    1.48 +{
    1.49 +}
    1.50 +
    1.51 +nsSMILTimeValueSpec::~nsSMILTimeValueSpec()
    1.52 +{
    1.53 +  UnregisterFromReferencedElement(mReferencedElement.get());
    1.54 +  if (mEventListener) {
    1.55 +    mEventListener->Disconnect();
    1.56 +    mEventListener = nullptr;
    1.57 +  }
    1.58 +}
    1.59 +
    1.60 +nsresult
    1.61 +nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
    1.62 +                             Element* aContextNode)
    1.63 +{
    1.64 +  nsSMILTimeValueSpecParams params;
    1.65 +
    1.66 +  if (!nsSMILParserUtils::ParseTimeValueSpecParams(aStringSpec, params))
    1.67 +    return NS_ERROR_FAILURE;
    1.68 +
    1.69 +  mParams = params;
    1.70 +
    1.71 +  // According to SMIL 3.0:
    1.72 +  //   The special value "indefinite" does not yield an instance time in the
    1.73 +  //   begin list. It will, however yield a single instance with the value
    1.74 +  //   "indefinite" in an end list. This value is not removed by a reset.
    1.75 +  if (mParams.mType == nsSMILTimeValueSpecParams::OFFSET ||
    1.76 +      (!mIsBegin && mParams.mType == nsSMILTimeValueSpecParams::INDEFINITE)) {
    1.77 +    mOwner->AddInstanceTime(new nsSMILInstanceTime(mParams.mOffset), mIsBegin);
    1.78 +  }
    1.79 +
    1.80 +  // Fill in the event symbol to simplify handling later
    1.81 +  if (mParams.mType == nsSMILTimeValueSpecParams::REPEAT) {
    1.82 +    mParams.mEventSymbol = nsGkAtoms::repeatEvent;
    1.83 +  } else if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
    1.84 +    mParams.mEventSymbol = nsGkAtoms::keypress;
    1.85 +  }
    1.86 +
    1.87 +  ResolveReferences(aContextNode);
    1.88 +
    1.89 +  return NS_OK;
    1.90 +}
    1.91 +
    1.92 +void
    1.93 +nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
    1.94 +{
    1.95 +  if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE && !IsEventBased())
    1.96 +    return;
    1.97 +
    1.98 +  NS_ABORT_IF_FALSE(aContextNode,
    1.99 +      "null context node for resolving timing references against");
   1.100 +
   1.101 +  // If we're not bound to the document yet, don't worry, we'll get called again
   1.102 +  // when that happens
   1.103 +  if (!aContextNode->IsInDoc())
   1.104 +    return;
   1.105 +
   1.106 +  // Hold ref to the old element so that it isn't destroyed in between resetting
   1.107 +  // the referenced element and using the pointer to update the referenced
   1.108 +  // element.
   1.109 +  nsRefPtr<Element> oldReferencedElement = mReferencedElement.get();
   1.110 +
   1.111 +  if (mParams.mDependentElemID) {
   1.112 +    mReferencedElement.ResetWithID(aContextNode,
   1.113 +        nsDependentAtomString(mParams.mDependentElemID));
   1.114 +  } else if (mParams.mType == nsSMILTimeValueSpecParams::EVENT) {
   1.115 +    Element* target = mOwner->GetTargetElement();
   1.116 +    mReferencedElement.ResetWithElement(target);
   1.117 +  } else if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
   1.118 +    nsIDocument* doc = aContextNode->GetCurrentDoc();
   1.119 +    NS_ABORT_IF_FALSE(doc, "We are in the document but current doc is null");
   1.120 +    mReferencedElement.ResetWithElement(doc->GetRootElement());
   1.121 +  } else {
   1.122 +    NS_ABORT_IF_FALSE(false, "Syncbase or repeat spec without ID");
   1.123 +  }
   1.124 +  UpdateReferencedElement(oldReferencedElement, mReferencedElement.get());
   1.125 +}
   1.126 +
   1.127 +bool
   1.128 +nsSMILTimeValueSpec::IsEventBased() const
   1.129 +{
   1.130 +  return mParams.mType == nsSMILTimeValueSpecParams::EVENT ||
   1.131 +         mParams.mType == nsSMILTimeValueSpecParams::REPEAT ||
   1.132 +         mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY;
   1.133 +}
   1.134 +
   1.135 +void
   1.136 +nsSMILTimeValueSpec::HandleNewInterval(nsSMILInterval& aInterval,
   1.137 +                                       const nsSMILTimeContainer* aSrcContainer)
   1.138 +{
   1.139 +  const nsSMILInstanceTime& baseInstance = mParams.mSyncBegin
   1.140 +    ? *aInterval.Begin() : *aInterval.End();
   1.141 +  nsSMILTimeValue newTime =
   1.142 +    ConvertBetweenTimeContainers(baseInstance.Time(), aSrcContainer);
   1.143 +
   1.144 +  // Apply offset
   1.145 +  if (!ApplyOffset(newTime)) {
   1.146 +    NS_WARNING("New time overflows nsSMILTime, ignoring");
   1.147 +    return;
   1.148 +  }
   1.149 +
   1.150 +  // Create the instance time and register it with the interval
   1.151 +  nsRefPtr<nsSMILInstanceTime> newInstance =
   1.152 +    new nsSMILInstanceTime(newTime, nsSMILInstanceTime::SOURCE_SYNCBASE, this,
   1.153 +                           &aInterval);
   1.154 +  mOwner->AddInstanceTime(newInstance, mIsBegin);
   1.155 +}
   1.156 +
   1.157 +void
   1.158 +nsSMILTimeValueSpec::HandleTargetElementChange(Element* aNewTarget)
   1.159 +{
   1.160 +  if (!IsEventBased() || mParams.mDependentElemID)
   1.161 +    return;
   1.162 +
   1.163 +  mReferencedElement.ResetWithElement(aNewTarget);
   1.164 +}
   1.165 +
   1.166 +void
   1.167 +nsSMILTimeValueSpec::HandleChangedInstanceTime(
   1.168 +    const nsSMILInstanceTime& aBaseTime,
   1.169 +    const nsSMILTimeContainer* aSrcContainer,
   1.170 +    nsSMILInstanceTime& aInstanceTimeToUpdate,
   1.171 +    bool aObjectChanged)
   1.172 +{
   1.173 +  // If the instance time is fixed (e.g. because it's being used as the begin
   1.174 +  // time of an active or postactive interval) we just ignore the change.
   1.175 +  if (aInstanceTimeToUpdate.IsFixedTime())
   1.176 +    return;
   1.177 +
   1.178 +  nsSMILTimeValue updatedTime =
   1.179 +    ConvertBetweenTimeContainers(aBaseTime.Time(), aSrcContainer);
   1.180 +
   1.181 +  // Apply offset
   1.182 +  if (!ApplyOffset(updatedTime)) {
   1.183 +    NS_WARNING("Updated time overflows nsSMILTime, ignoring");
   1.184 +    return;
   1.185 +  }
   1.186 +
   1.187 +  // The timed element that owns the instance time does the updating so it can
   1.188 +  // re-sort its array of instance times more efficiently
   1.189 +  if (aInstanceTimeToUpdate.Time() != updatedTime || aObjectChanged) {
   1.190 +    mOwner->UpdateInstanceTime(&aInstanceTimeToUpdate, updatedTime, mIsBegin);
   1.191 +  }
   1.192 +}
   1.193 +
   1.194 +void
   1.195 +nsSMILTimeValueSpec::HandleDeletedInstanceTime(
   1.196 +    nsSMILInstanceTime &aInstanceTime)
   1.197 +{
   1.198 +  mOwner->RemoveInstanceTime(&aInstanceTime, mIsBegin);
   1.199 +}
   1.200 +
   1.201 +bool
   1.202 +nsSMILTimeValueSpec::DependsOnBegin() const
   1.203 +{
   1.204 +  return mParams.mSyncBegin;
   1.205 +}
   1.206 +
   1.207 +void
   1.208 +nsSMILTimeValueSpec::Traverse(nsCycleCollectionTraversalCallback* aCallback)
   1.209 +{
   1.210 +  mReferencedElement.Traverse(aCallback);
   1.211 +}
   1.212 +
   1.213 +void
   1.214 +nsSMILTimeValueSpec::Unlink()
   1.215 +{
   1.216 +  UnregisterFromReferencedElement(mReferencedElement.get());
   1.217 +  mReferencedElement.Unlink();
   1.218 +}
   1.219 +
   1.220 +//----------------------------------------------------------------------
   1.221 +// Implementation helpers
   1.222 +
   1.223 +void
   1.224 +nsSMILTimeValueSpec::UpdateReferencedElement(Element* aFrom, Element* aTo)
   1.225 +{
   1.226 +  if (aFrom == aTo)
   1.227 +    return;
   1.228 +
   1.229 +  UnregisterFromReferencedElement(aFrom);
   1.230 +
   1.231 +  switch (mParams.mType)
   1.232 +  {
   1.233 +  case nsSMILTimeValueSpecParams::SYNCBASE:
   1.234 +    {
   1.235 +      nsSMILTimedElement* to = GetTimedElement(aTo);
   1.236 +      if (to) {
   1.237 +        to->AddDependent(*this);
   1.238 +      }
   1.239 +    }
   1.240 +    break;
   1.241 +
   1.242 +  case nsSMILTimeValueSpecParams::EVENT:
   1.243 +  case nsSMILTimeValueSpecParams::REPEAT:
   1.244 +  case nsSMILTimeValueSpecParams::ACCESSKEY:
   1.245 +    RegisterEventListener(aTo);
   1.246 +    break;
   1.247 +
   1.248 +  default:
   1.249 +    // not a referencing-type
   1.250 +    break;
   1.251 +  }
   1.252 +}
   1.253 +
   1.254 +void
   1.255 +nsSMILTimeValueSpec::UnregisterFromReferencedElement(Element* aElement)
   1.256 +{
   1.257 +  if (!aElement)
   1.258 +    return;
   1.259 +
   1.260 +  if (mParams.mType == nsSMILTimeValueSpecParams::SYNCBASE) {
   1.261 +    nsSMILTimedElement* timedElement = GetTimedElement(aElement);
   1.262 +    if (timedElement) {
   1.263 +      timedElement->RemoveDependent(*this);
   1.264 +    }
   1.265 +    mOwner->RemoveInstanceTimesForCreator(this, mIsBegin);
   1.266 +  } else if (IsEventBased()) {
   1.267 +    UnregisterEventListener(aElement);
   1.268 +  }
   1.269 +}
   1.270 +
   1.271 +nsSMILTimedElement*
   1.272 +nsSMILTimeValueSpec::GetTimedElement(Element* aElement)
   1.273 +{
   1.274 +  return aElement && aElement->IsNodeOfType(nsINode::eANIMATION) ?
   1.275 +    &static_cast<SVGAnimationElement*>(aElement)->TimedElement() : nullptr;
   1.276 +}
   1.277 +
   1.278 +// Indicates whether we're allowed to register an event-listener
   1.279 +// when scripting is disabled.
   1.280 +bool
   1.281 +nsSMILTimeValueSpec::IsWhitelistedEvent()
   1.282 +{
   1.283 +  // The category of (SMIL-specific) "repeat(n)" events are allowed.
   1.284 +  if (mParams.mType == nsSMILTimeValueSpecParams::REPEAT) {
   1.285 +    return true;
   1.286 +  }
   1.287 +
   1.288 +  // A specific list of other SMIL-related events are allowed, too.
   1.289 +  if (mParams.mType == nsSMILTimeValueSpecParams::EVENT &&
   1.290 +      (mParams.mEventSymbol == nsGkAtoms::repeat ||
   1.291 +       mParams.mEventSymbol == nsGkAtoms::repeatEvent ||
   1.292 +       mParams.mEventSymbol == nsGkAtoms::beginEvent ||
   1.293 +       mParams.mEventSymbol == nsGkAtoms::endEvent)) {
   1.294 +    return true;
   1.295 +  }
   1.296 +
   1.297 +  return false;
   1.298 +}
   1.299 +
   1.300 +void
   1.301 +nsSMILTimeValueSpec::RegisterEventListener(Element* aTarget)
   1.302 +{
   1.303 +  NS_ABORT_IF_FALSE(IsEventBased(),
   1.304 +    "Attempting to register event-listener for unexpected nsSMILTimeValueSpec"
   1.305 +    " type");
   1.306 +  NS_ABORT_IF_FALSE(mParams.mEventSymbol,
   1.307 +    "Attempting to register event-listener but there is no event name");
   1.308 +
   1.309 +  if (!aTarget)
   1.310 +    return;
   1.311 +
   1.312 +  // When script is disabled, only allow registration for whitelisted events.
   1.313 +  if (!aTarget->GetOwnerDocument()->IsScriptEnabled() &&
   1.314 +      !IsWhitelistedEvent()) {
   1.315 +    return;
   1.316 +  }
   1.317 +
   1.318 +  if (!mEventListener) {
   1.319 +    mEventListener = new EventListener(this);
   1.320 +  }
   1.321 +
   1.322 +  EventListenerManager* elm = GetEventListenerManager(aTarget);
   1.323 +  if (!elm)
   1.324 +    return;
   1.325 +
   1.326 +  elm->AddEventListenerByType(mEventListener,
   1.327 +                              nsDependentAtomString(mParams.mEventSymbol),
   1.328 +                              AllEventsAtSystemGroupBubble());
   1.329 +}
   1.330 +
   1.331 +void
   1.332 +nsSMILTimeValueSpec::UnregisterEventListener(Element* aTarget)
   1.333 +{
   1.334 +  if (!aTarget || !mEventListener)
   1.335 +    return;
   1.336 +
   1.337 +  EventListenerManager* elm = GetEventListenerManager(aTarget);
   1.338 +  if (!elm)
   1.339 +    return;
   1.340 +
   1.341 +  elm->RemoveEventListenerByType(mEventListener,
   1.342 +                                 nsDependentAtomString(mParams.mEventSymbol),
   1.343 +                                 AllEventsAtSystemGroupBubble());
   1.344 +}
   1.345 +
   1.346 +EventListenerManager*
   1.347 +nsSMILTimeValueSpec::GetEventListenerManager(Element* aTarget)
   1.348 +{
   1.349 +  NS_ABORT_IF_FALSE(aTarget, "null target; can't get EventListenerManager");
   1.350 +
   1.351 +  nsCOMPtr<EventTarget> target;
   1.352 +
   1.353 +  if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
   1.354 +    nsIDocument* doc = aTarget->GetCurrentDoc();
   1.355 +    if (!doc)
   1.356 +      return nullptr;
   1.357 +    nsPIDOMWindow* win = doc->GetWindow();
   1.358 +    if (!win)
   1.359 +      return nullptr;
   1.360 +    target = do_QueryInterface(win);
   1.361 +  } else {
   1.362 +    target = aTarget;
   1.363 +  }
   1.364 +  if (!target)
   1.365 +    return nullptr;
   1.366 +
   1.367 +  return target->GetOrCreateListenerManager();
   1.368 +}
   1.369 +
   1.370 +void
   1.371 +nsSMILTimeValueSpec::HandleEvent(nsIDOMEvent* aEvent)
   1.372 +{
   1.373 +  NS_ABORT_IF_FALSE(mEventListener, "Got event without an event listener");
   1.374 +  NS_ABORT_IF_FALSE(IsEventBased(),
   1.375 +                    "Got event for non-event nsSMILTimeValueSpec");
   1.376 +  NS_ABORT_IF_FALSE(aEvent, "No event supplied");
   1.377 +
   1.378 +  // XXX In the long run we should get the time from the event itself which will
   1.379 +  // store the time in global document time which we'll need to convert to our
   1.380 +  // time container
   1.381 +  nsSMILTimeContainer* container = mOwner->GetTimeContainer();
   1.382 +  if (!container)
   1.383 +    return;
   1.384 +
   1.385 +  if (!CheckEventDetail(aEvent))
   1.386 +    return;
   1.387 +
   1.388 +  nsSMILTime currentTime = container->GetCurrentTime();
   1.389 +  nsSMILTimeValue newTime(currentTime);
   1.390 +  if (!ApplyOffset(newTime)) {
   1.391 +    NS_WARNING("New time generated from event overflows nsSMILTime, ignoring");
   1.392 +    return;
   1.393 +  }
   1.394 +
   1.395 +  nsRefPtr<nsSMILInstanceTime> newInstance =
   1.396 +    new nsSMILInstanceTime(newTime, nsSMILInstanceTime::SOURCE_EVENT);
   1.397 +  mOwner->AddInstanceTime(newInstance, mIsBegin);
   1.398 +}
   1.399 +
   1.400 +bool
   1.401 +nsSMILTimeValueSpec::CheckEventDetail(nsIDOMEvent *aEvent)
   1.402 +{
   1.403 +  switch (mParams.mType)
   1.404 +  {
   1.405 +  case nsSMILTimeValueSpecParams::REPEAT:
   1.406 +    return CheckRepeatEventDetail(aEvent);
   1.407 +
   1.408 +  case nsSMILTimeValueSpecParams::ACCESSKEY:
   1.409 +    return CheckAccessKeyEventDetail(aEvent);
   1.410 +
   1.411 +  default:
   1.412 +    // nothing to check
   1.413 +    return true;
   1.414 +  }
   1.415 +}
   1.416 +
   1.417 +bool
   1.418 +nsSMILTimeValueSpec::CheckRepeatEventDetail(nsIDOMEvent *aEvent)
   1.419 +{
   1.420 +  nsCOMPtr<nsIDOMTimeEvent> timeEvent = do_QueryInterface(aEvent);
   1.421 +  if (!timeEvent) {
   1.422 +    NS_WARNING("Received a repeat event that was not a DOMTimeEvent");
   1.423 +    return false;
   1.424 +  }
   1.425 +
   1.426 +  int32_t detail;
   1.427 +  timeEvent->GetDetail(&detail);
   1.428 +  return detail > 0 && (uint32_t)detail == mParams.mRepeatIterationOrAccessKey;
   1.429 +}
   1.430 +
   1.431 +bool
   1.432 +nsSMILTimeValueSpec::CheckAccessKeyEventDetail(nsIDOMEvent *aEvent)
   1.433 +{
   1.434 +  nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
   1.435 +  if (!keyEvent) {
   1.436 +    NS_WARNING("Received an accesskey event that was not a DOMKeyEvent");
   1.437 +    return false;
   1.438 +  }
   1.439 +
   1.440 +  // Ignore the key event if any modifier keys are pressed UNLESS we're matching
   1.441 +  // on the charCode in which case we ignore the state of the shift and alt keys
   1.442 +  // since they might be needed to generate the character in question.
   1.443 +  bool isCtrl;
   1.444 +  bool isMeta;
   1.445 +  keyEvent->GetCtrlKey(&isCtrl);
   1.446 +  keyEvent->GetMetaKey(&isMeta);
   1.447 +  if (isCtrl || isMeta)
   1.448 +    return false;
   1.449 +
   1.450 +  uint32_t code;
   1.451 +  keyEvent->GetCharCode(&code);
   1.452 +  if (code)
   1.453 +    return code == mParams.mRepeatIterationOrAccessKey;
   1.454 +
   1.455 +  // Only match on the keyCode if it corresponds to some ASCII character that
   1.456 +  // does not produce a charCode.
   1.457 +  // In this case we can safely bail out if either alt or shift is pressed since
   1.458 +  // they won't already be incorporated into the keyCode unlike the charCode.
   1.459 +  bool isAlt;
   1.460 +  bool isShift;
   1.461 +  keyEvent->GetAltKey(&isAlt);
   1.462 +  keyEvent->GetShiftKey(&isShift);
   1.463 +  if (isAlt || isShift)
   1.464 +    return false;
   1.465 +
   1.466 +  keyEvent->GetKeyCode(&code);
   1.467 +  switch (code)
   1.468 +  {
   1.469 +  case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
   1.470 +    return mParams.mRepeatIterationOrAccessKey == 0x08;
   1.471 +
   1.472 +  case nsIDOMKeyEvent::DOM_VK_RETURN:
   1.473 +    return mParams.mRepeatIterationOrAccessKey == 0x0A ||
   1.474 +           mParams.mRepeatIterationOrAccessKey == 0x0D;
   1.475 +
   1.476 +  case nsIDOMKeyEvent::DOM_VK_ESCAPE:
   1.477 +    return mParams.mRepeatIterationOrAccessKey == 0x1B;
   1.478 +
   1.479 +  case nsIDOMKeyEvent::DOM_VK_DELETE:
   1.480 +    return mParams.mRepeatIterationOrAccessKey == 0x7F;
   1.481 +
   1.482 +  default:
   1.483 +    return false;
   1.484 +  }
   1.485 +}
   1.486 +
   1.487 +nsSMILTimeValue
   1.488 +nsSMILTimeValueSpec::ConvertBetweenTimeContainers(
   1.489 +    const nsSMILTimeValue& aSrcTime,
   1.490 +    const nsSMILTimeContainer* aSrcContainer)
   1.491 +{
   1.492 +  // If the source time is either indefinite or unresolved the result is going
   1.493 +  // to be the same
   1.494 +  if (!aSrcTime.IsDefinite())
   1.495 +    return aSrcTime;
   1.496 +
   1.497 +  // Convert from source time container to our parent time container
   1.498 +  const nsSMILTimeContainer* dstContainer = mOwner->GetTimeContainer();
   1.499 +  if (dstContainer == aSrcContainer)
   1.500 +    return aSrcTime;
   1.501 +
   1.502 +  // If one of the elements is not attached to a time container then we can't do
   1.503 +  // any meaningful conversion
   1.504 +  if (!aSrcContainer || !dstContainer)
   1.505 +    return nsSMILTimeValue(); // unresolved
   1.506 +
   1.507 +  nsSMILTimeValue docTime =
   1.508 +    aSrcContainer->ContainerToParentTime(aSrcTime.GetMillis());
   1.509 +
   1.510 +  if (docTime.IsIndefinite())
   1.511 +    // This will happen if the source container is paused and we have a future
   1.512 +    // time. Just return the indefinite time.
   1.513 +    return docTime;
   1.514 +
   1.515 +  NS_ABORT_IF_FALSE(docTime.IsDefinite(),
   1.516 +    "ContainerToParentTime gave us an unresolved or indefinite time");
   1.517 +
   1.518 +  return dstContainer->ParentToContainerTime(docTime.GetMillis());
   1.519 +}
   1.520 +
   1.521 +bool
   1.522 +nsSMILTimeValueSpec::ApplyOffset(nsSMILTimeValue& aTime) const
   1.523 +{
   1.524 +  // indefinite + offset = indefinite. Likewise for unresolved times.
   1.525 +  if (!aTime.IsDefinite()) {
   1.526 +    return true;
   1.527 +  }
   1.528 +
   1.529 +  double resultAsDouble =
   1.530 +    (double)aTime.GetMillis() + mParams.mOffset.GetMillis();
   1.531 +  if (resultAsDouble > std::numeric_limits<nsSMILTime>::max() ||
   1.532 +      resultAsDouble < std::numeric_limits<nsSMILTime>::min()) {
   1.533 +    return false;
   1.534 +  }
   1.535 +  aTime.SetMillis(aTime.GetMillis() + mParams.mOffset.GetMillis());
   1.536 +  return true;
   1.537 +}

mercurial