dom/smil/nsSMILTimeValueSpec.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/EventListenerManager.h"
michael@0 7 #include "mozilla/dom/SVGAnimationElement.h"
michael@0 8 #include "nsSMILTimeValueSpec.h"
michael@0 9 #include "nsSMILInterval.h"
michael@0 10 #include "nsSMILTimeContainer.h"
michael@0 11 #include "nsSMILTimeValue.h"
michael@0 12 #include "nsSMILTimedElement.h"
michael@0 13 #include "nsSMILInstanceTime.h"
michael@0 14 #include "nsSMILParserUtils.h"
michael@0 15 #include "nsIDOMKeyEvent.h"
michael@0 16 #include "nsIDOMTimeEvent.h"
michael@0 17 #include "nsString.h"
michael@0 18 #include <limits>
michael@0 19
michael@0 20 using namespace mozilla;
michael@0 21 using namespace mozilla::dom;
michael@0 22
michael@0 23 //----------------------------------------------------------------------
michael@0 24 // Nested class: EventListener
michael@0 25
michael@0 26 NS_IMPL_ISUPPORTS(nsSMILTimeValueSpec::EventListener, nsIDOMEventListener)
michael@0 27
michael@0 28 NS_IMETHODIMP
michael@0 29 nsSMILTimeValueSpec::EventListener::HandleEvent(nsIDOMEvent* aEvent)
michael@0 30 {
michael@0 31 if (mSpec) {
michael@0 32 mSpec->HandleEvent(aEvent);
michael@0 33 }
michael@0 34 return NS_OK;
michael@0 35 }
michael@0 36
michael@0 37 //----------------------------------------------------------------------
michael@0 38 // Implementation
michael@0 39
michael@0 40 nsSMILTimeValueSpec::nsSMILTimeValueSpec(nsSMILTimedElement& aOwner,
michael@0 41 bool aIsBegin)
michael@0 42 : mOwner(&aOwner),
michael@0 43 mIsBegin(aIsBegin),
michael@0 44 mReferencedElement(MOZ_THIS_IN_INITIALIZER_LIST())
michael@0 45 {
michael@0 46 }
michael@0 47
michael@0 48 nsSMILTimeValueSpec::~nsSMILTimeValueSpec()
michael@0 49 {
michael@0 50 UnregisterFromReferencedElement(mReferencedElement.get());
michael@0 51 if (mEventListener) {
michael@0 52 mEventListener->Disconnect();
michael@0 53 mEventListener = nullptr;
michael@0 54 }
michael@0 55 }
michael@0 56
michael@0 57 nsresult
michael@0 58 nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
michael@0 59 Element* aContextNode)
michael@0 60 {
michael@0 61 nsSMILTimeValueSpecParams params;
michael@0 62
michael@0 63 if (!nsSMILParserUtils::ParseTimeValueSpecParams(aStringSpec, params))
michael@0 64 return NS_ERROR_FAILURE;
michael@0 65
michael@0 66 mParams = params;
michael@0 67
michael@0 68 // According to SMIL 3.0:
michael@0 69 // The special value "indefinite" does not yield an instance time in the
michael@0 70 // begin list. It will, however yield a single instance with the value
michael@0 71 // "indefinite" in an end list. This value is not removed by a reset.
michael@0 72 if (mParams.mType == nsSMILTimeValueSpecParams::OFFSET ||
michael@0 73 (!mIsBegin && mParams.mType == nsSMILTimeValueSpecParams::INDEFINITE)) {
michael@0 74 mOwner->AddInstanceTime(new nsSMILInstanceTime(mParams.mOffset), mIsBegin);
michael@0 75 }
michael@0 76
michael@0 77 // Fill in the event symbol to simplify handling later
michael@0 78 if (mParams.mType == nsSMILTimeValueSpecParams::REPEAT) {
michael@0 79 mParams.mEventSymbol = nsGkAtoms::repeatEvent;
michael@0 80 } else if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
michael@0 81 mParams.mEventSymbol = nsGkAtoms::keypress;
michael@0 82 }
michael@0 83
michael@0 84 ResolveReferences(aContextNode);
michael@0 85
michael@0 86 return NS_OK;
michael@0 87 }
michael@0 88
michael@0 89 void
michael@0 90 nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
michael@0 91 {
michael@0 92 if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE && !IsEventBased())
michael@0 93 return;
michael@0 94
michael@0 95 NS_ABORT_IF_FALSE(aContextNode,
michael@0 96 "null context node for resolving timing references against");
michael@0 97
michael@0 98 // If we're not bound to the document yet, don't worry, we'll get called again
michael@0 99 // when that happens
michael@0 100 if (!aContextNode->IsInDoc())
michael@0 101 return;
michael@0 102
michael@0 103 // Hold ref to the old element so that it isn't destroyed in between resetting
michael@0 104 // the referenced element and using the pointer to update the referenced
michael@0 105 // element.
michael@0 106 nsRefPtr<Element> oldReferencedElement = mReferencedElement.get();
michael@0 107
michael@0 108 if (mParams.mDependentElemID) {
michael@0 109 mReferencedElement.ResetWithID(aContextNode,
michael@0 110 nsDependentAtomString(mParams.mDependentElemID));
michael@0 111 } else if (mParams.mType == nsSMILTimeValueSpecParams::EVENT) {
michael@0 112 Element* target = mOwner->GetTargetElement();
michael@0 113 mReferencedElement.ResetWithElement(target);
michael@0 114 } else if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
michael@0 115 nsIDocument* doc = aContextNode->GetCurrentDoc();
michael@0 116 NS_ABORT_IF_FALSE(doc, "We are in the document but current doc is null");
michael@0 117 mReferencedElement.ResetWithElement(doc->GetRootElement());
michael@0 118 } else {
michael@0 119 NS_ABORT_IF_FALSE(false, "Syncbase or repeat spec without ID");
michael@0 120 }
michael@0 121 UpdateReferencedElement(oldReferencedElement, mReferencedElement.get());
michael@0 122 }
michael@0 123
michael@0 124 bool
michael@0 125 nsSMILTimeValueSpec::IsEventBased() const
michael@0 126 {
michael@0 127 return mParams.mType == nsSMILTimeValueSpecParams::EVENT ||
michael@0 128 mParams.mType == nsSMILTimeValueSpecParams::REPEAT ||
michael@0 129 mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY;
michael@0 130 }
michael@0 131
michael@0 132 void
michael@0 133 nsSMILTimeValueSpec::HandleNewInterval(nsSMILInterval& aInterval,
michael@0 134 const nsSMILTimeContainer* aSrcContainer)
michael@0 135 {
michael@0 136 const nsSMILInstanceTime& baseInstance = mParams.mSyncBegin
michael@0 137 ? *aInterval.Begin() : *aInterval.End();
michael@0 138 nsSMILTimeValue newTime =
michael@0 139 ConvertBetweenTimeContainers(baseInstance.Time(), aSrcContainer);
michael@0 140
michael@0 141 // Apply offset
michael@0 142 if (!ApplyOffset(newTime)) {
michael@0 143 NS_WARNING("New time overflows nsSMILTime, ignoring");
michael@0 144 return;
michael@0 145 }
michael@0 146
michael@0 147 // Create the instance time and register it with the interval
michael@0 148 nsRefPtr<nsSMILInstanceTime> newInstance =
michael@0 149 new nsSMILInstanceTime(newTime, nsSMILInstanceTime::SOURCE_SYNCBASE, this,
michael@0 150 &aInterval);
michael@0 151 mOwner->AddInstanceTime(newInstance, mIsBegin);
michael@0 152 }
michael@0 153
michael@0 154 void
michael@0 155 nsSMILTimeValueSpec::HandleTargetElementChange(Element* aNewTarget)
michael@0 156 {
michael@0 157 if (!IsEventBased() || mParams.mDependentElemID)
michael@0 158 return;
michael@0 159
michael@0 160 mReferencedElement.ResetWithElement(aNewTarget);
michael@0 161 }
michael@0 162
michael@0 163 void
michael@0 164 nsSMILTimeValueSpec::HandleChangedInstanceTime(
michael@0 165 const nsSMILInstanceTime& aBaseTime,
michael@0 166 const nsSMILTimeContainer* aSrcContainer,
michael@0 167 nsSMILInstanceTime& aInstanceTimeToUpdate,
michael@0 168 bool aObjectChanged)
michael@0 169 {
michael@0 170 // If the instance time is fixed (e.g. because it's being used as the begin
michael@0 171 // time of an active or postactive interval) we just ignore the change.
michael@0 172 if (aInstanceTimeToUpdate.IsFixedTime())
michael@0 173 return;
michael@0 174
michael@0 175 nsSMILTimeValue updatedTime =
michael@0 176 ConvertBetweenTimeContainers(aBaseTime.Time(), aSrcContainer);
michael@0 177
michael@0 178 // Apply offset
michael@0 179 if (!ApplyOffset(updatedTime)) {
michael@0 180 NS_WARNING("Updated time overflows nsSMILTime, ignoring");
michael@0 181 return;
michael@0 182 }
michael@0 183
michael@0 184 // The timed element that owns the instance time does the updating so it can
michael@0 185 // re-sort its array of instance times more efficiently
michael@0 186 if (aInstanceTimeToUpdate.Time() != updatedTime || aObjectChanged) {
michael@0 187 mOwner->UpdateInstanceTime(&aInstanceTimeToUpdate, updatedTime, mIsBegin);
michael@0 188 }
michael@0 189 }
michael@0 190
michael@0 191 void
michael@0 192 nsSMILTimeValueSpec::HandleDeletedInstanceTime(
michael@0 193 nsSMILInstanceTime &aInstanceTime)
michael@0 194 {
michael@0 195 mOwner->RemoveInstanceTime(&aInstanceTime, mIsBegin);
michael@0 196 }
michael@0 197
michael@0 198 bool
michael@0 199 nsSMILTimeValueSpec::DependsOnBegin() const
michael@0 200 {
michael@0 201 return mParams.mSyncBegin;
michael@0 202 }
michael@0 203
michael@0 204 void
michael@0 205 nsSMILTimeValueSpec::Traverse(nsCycleCollectionTraversalCallback* aCallback)
michael@0 206 {
michael@0 207 mReferencedElement.Traverse(aCallback);
michael@0 208 }
michael@0 209
michael@0 210 void
michael@0 211 nsSMILTimeValueSpec::Unlink()
michael@0 212 {
michael@0 213 UnregisterFromReferencedElement(mReferencedElement.get());
michael@0 214 mReferencedElement.Unlink();
michael@0 215 }
michael@0 216
michael@0 217 //----------------------------------------------------------------------
michael@0 218 // Implementation helpers
michael@0 219
michael@0 220 void
michael@0 221 nsSMILTimeValueSpec::UpdateReferencedElement(Element* aFrom, Element* aTo)
michael@0 222 {
michael@0 223 if (aFrom == aTo)
michael@0 224 return;
michael@0 225
michael@0 226 UnregisterFromReferencedElement(aFrom);
michael@0 227
michael@0 228 switch (mParams.mType)
michael@0 229 {
michael@0 230 case nsSMILTimeValueSpecParams::SYNCBASE:
michael@0 231 {
michael@0 232 nsSMILTimedElement* to = GetTimedElement(aTo);
michael@0 233 if (to) {
michael@0 234 to->AddDependent(*this);
michael@0 235 }
michael@0 236 }
michael@0 237 break;
michael@0 238
michael@0 239 case nsSMILTimeValueSpecParams::EVENT:
michael@0 240 case nsSMILTimeValueSpecParams::REPEAT:
michael@0 241 case nsSMILTimeValueSpecParams::ACCESSKEY:
michael@0 242 RegisterEventListener(aTo);
michael@0 243 break;
michael@0 244
michael@0 245 default:
michael@0 246 // not a referencing-type
michael@0 247 break;
michael@0 248 }
michael@0 249 }
michael@0 250
michael@0 251 void
michael@0 252 nsSMILTimeValueSpec::UnregisterFromReferencedElement(Element* aElement)
michael@0 253 {
michael@0 254 if (!aElement)
michael@0 255 return;
michael@0 256
michael@0 257 if (mParams.mType == nsSMILTimeValueSpecParams::SYNCBASE) {
michael@0 258 nsSMILTimedElement* timedElement = GetTimedElement(aElement);
michael@0 259 if (timedElement) {
michael@0 260 timedElement->RemoveDependent(*this);
michael@0 261 }
michael@0 262 mOwner->RemoveInstanceTimesForCreator(this, mIsBegin);
michael@0 263 } else if (IsEventBased()) {
michael@0 264 UnregisterEventListener(aElement);
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 nsSMILTimedElement*
michael@0 269 nsSMILTimeValueSpec::GetTimedElement(Element* aElement)
michael@0 270 {
michael@0 271 return aElement && aElement->IsNodeOfType(nsINode::eANIMATION) ?
michael@0 272 &static_cast<SVGAnimationElement*>(aElement)->TimedElement() : nullptr;
michael@0 273 }
michael@0 274
michael@0 275 // Indicates whether we're allowed to register an event-listener
michael@0 276 // when scripting is disabled.
michael@0 277 bool
michael@0 278 nsSMILTimeValueSpec::IsWhitelistedEvent()
michael@0 279 {
michael@0 280 // The category of (SMIL-specific) "repeat(n)" events are allowed.
michael@0 281 if (mParams.mType == nsSMILTimeValueSpecParams::REPEAT) {
michael@0 282 return true;
michael@0 283 }
michael@0 284
michael@0 285 // A specific list of other SMIL-related events are allowed, too.
michael@0 286 if (mParams.mType == nsSMILTimeValueSpecParams::EVENT &&
michael@0 287 (mParams.mEventSymbol == nsGkAtoms::repeat ||
michael@0 288 mParams.mEventSymbol == nsGkAtoms::repeatEvent ||
michael@0 289 mParams.mEventSymbol == nsGkAtoms::beginEvent ||
michael@0 290 mParams.mEventSymbol == nsGkAtoms::endEvent)) {
michael@0 291 return true;
michael@0 292 }
michael@0 293
michael@0 294 return false;
michael@0 295 }
michael@0 296
michael@0 297 void
michael@0 298 nsSMILTimeValueSpec::RegisterEventListener(Element* aTarget)
michael@0 299 {
michael@0 300 NS_ABORT_IF_FALSE(IsEventBased(),
michael@0 301 "Attempting to register event-listener for unexpected nsSMILTimeValueSpec"
michael@0 302 " type");
michael@0 303 NS_ABORT_IF_FALSE(mParams.mEventSymbol,
michael@0 304 "Attempting to register event-listener but there is no event name");
michael@0 305
michael@0 306 if (!aTarget)
michael@0 307 return;
michael@0 308
michael@0 309 // When script is disabled, only allow registration for whitelisted events.
michael@0 310 if (!aTarget->GetOwnerDocument()->IsScriptEnabled() &&
michael@0 311 !IsWhitelistedEvent()) {
michael@0 312 return;
michael@0 313 }
michael@0 314
michael@0 315 if (!mEventListener) {
michael@0 316 mEventListener = new EventListener(this);
michael@0 317 }
michael@0 318
michael@0 319 EventListenerManager* elm = GetEventListenerManager(aTarget);
michael@0 320 if (!elm)
michael@0 321 return;
michael@0 322
michael@0 323 elm->AddEventListenerByType(mEventListener,
michael@0 324 nsDependentAtomString(mParams.mEventSymbol),
michael@0 325 AllEventsAtSystemGroupBubble());
michael@0 326 }
michael@0 327
michael@0 328 void
michael@0 329 nsSMILTimeValueSpec::UnregisterEventListener(Element* aTarget)
michael@0 330 {
michael@0 331 if (!aTarget || !mEventListener)
michael@0 332 return;
michael@0 333
michael@0 334 EventListenerManager* elm = GetEventListenerManager(aTarget);
michael@0 335 if (!elm)
michael@0 336 return;
michael@0 337
michael@0 338 elm->RemoveEventListenerByType(mEventListener,
michael@0 339 nsDependentAtomString(mParams.mEventSymbol),
michael@0 340 AllEventsAtSystemGroupBubble());
michael@0 341 }
michael@0 342
michael@0 343 EventListenerManager*
michael@0 344 nsSMILTimeValueSpec::GetEventListenerManager(Element* aTarget)
michael@0 345 {
michael@0 346 NS_ABORT_IF_FALSE(aTarget, "null target; can't get EventListenerManager");
michael@0 347
michael@0 348 nsCOMPtr<EventTarget> target;
michael@0 349
michael@0 350 if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
michael@0 351 nsIDocument* doc = aTarget->GetCurrentDoc();
michael@0 352 if (!doc)
michael@0 353 return nullptr;
michael@0 354 nsPIDOMWindow* win = doc->GetWindow();
michael@0 355 if (!win)
michael@0 356 return nullptr;
michael@0 357 target = do_QueryInterface(win);
michael@0 358 } else {
michael@0 359 target = aTarget;
michael@0 360 }
michael@0 361 if (!target)
michael@0 362 return nullptr;
michael@0 363
michael@0 364 return target->GetOrCreateListenerManager();
michael@0 365 }
michael@0 366
michael@0 367 void
michael@0 368 nsSMILTimeValueSpec::HandleEvent(nsIDOMEvent* aEvent)
michael@0 369 {
michael@0 370 NS_ABORT_IF_FALSE(mEventListener, "Got event without an event listener");
michael@0 371 NS_ABORT_IF_FALSE(IsEventBased(),
michael@0 372 "Got event for non-event nsSMILTimeValueSpec");
michael@0 373 NS_ABORT_IF_FALSE(aEvent, "No event supplied");
michael@0 374
michael@0 375 // XXX In the long run we should get the time from the event itself which will
michael@0 376 // store the time in global document time which we'll need to convert to our
michael@0 377 // time container
michael@0 378 nsSMILTimeContainer* container = mOwner->GetTimeContainer();
michael@0 379 if (!container)
michael@0 380 return;
michael@0 381
michael@0 382 if (!CheckEventDetail(aEvent))
michael@0 383 return;
michael@0 384
michael@0 385 nsSMILTime currentTime = container->GetCurrentTime();
michael@0 386 nsSMILTimeValue newTime(currentTime);
michael@0 387 if (!ApplyOffset(newTime)) {
michael@0 388 NS_WARNING("New time generated from event overflows nsSMILTime, ignoring");
michael@0 389 return;
michael@0 390 }
michael@0 391
michael@0 392 nsRefPtr<nsSMILInstanceTime> newInstance =
michael@0 393 new nsSMILInstanceTime(newTime, nsSMILInstanceTime::SOURCE_EVENT);
michael@0 394 mOwner->AddInstanceTime(newInstance, mIsBegin);
michael@0 395 }
michael@0 396
michael@0 397 bool
michael@0 398 nsSMILTimeValueSpec::CheckEventDetail(nsIDOMEvent *aEvent)
michael@0 399 {
michael@0 400 switch (mParams.mType)
michael@0 401 {
michael@0 402 case nsSMILTimeValueSpecParams::REPEAT:
michael@0 403 return CheckRepeatEventDetail(aEvent);
michael@0 404
michael@0 405 case nsSMILTimeValueSpecParams::ACCESSKEY:
michael@0 406 return CheckAccessKeyEventDetail(aEvent);
michael@0 407
michael@0 408 default:
michael@0 409 // nothing to check
michael@0 410 return true;
michael@0 411 }
michael@0 412 }
michael@0 413
michael@0 414 bool
michael@0 415 nsSMILTimeValueSpec::CheckRepeatEventDetail(nsIDOMEvent *aEvent)
michael@0 416 {
michael@0 417 nsCOMPtr<nsIDOMTimeEvent> timeEvent = do_QueryInterface(aEvent);
michael@0 418 if (!timeEvent) {
michael@0 419 NS_WARNING("Received a repeat event that was not a DOMTimeEvent");
michael@0 420 return false;
michael@0 421 }
michael@0 422
michael@0 423 int32_t detail;
michael@0 424 timeEvent->GetDetail(&detail);
michael@0 425 return detail > 0 && (uint32_t)detail == mParams.mRepeatIterationOrAccessKey;
michael@0 426 }
michael@0 427
michael@0 428 bool
michael@0 429 nsSMILTimeValueSpec::CheckAccessKeyEventDetail(nsIDOMEvent *aEvent)
michael@0 430 {
michael@0 431 nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
michael@0 432 if (!keyEvent) {
michael@0 433 NS_WARNING("Received an accesskey event that was not a DOMKeyEvent");
michael@0 434 return false;
michael@0 435 }
michael@0 436
michael@0 437 // Ignore the key event if any modifier keys are pressed UNLESS we're matching
michael@0 438 // on the charCode in which case we ignore the state of the shift and alt keys
michael@0 439 // since they might be needed to generate the character in question.
michael@0 440 bool isCtrl;
michael@0 441 bool isMeta;
michael@0 442 keyEvent->GetCtrlKey(&isCtrl);
michael@0 443 keyEvent->GetMetaKey(&isMeta);
michael@0 444 if (isCtrl || isMeta)
michael@0 445 return false;
michael@0 446
michael@0 447 uint32_t code;
michael@0 448 keyEvent->GetCharCode(&code);
michael@0 449 if (code)
michael@0 450 return code == mParams.mRepeatIterationOrAccessKey;
michael@0 451
michael@0 452 // Only match on the keyCode if it corresponds to some ASCII character that
michael@0 453 // does not produce a charCode.
michael@0 454 // In this case we can safely bail out if either alt or shift is pressed since
michael@0 455 // they won't already be incorporated into the keyCode unlike the charCode.
michael@0 456 bool isAlt;
michael@0 457 bool isShift;
michael@0 458 keyEvent->GetAltKey(&isAlt);
michael@0 459 keyEvent->GetShiftKey(&isShift);
michael@0 460 if (isAlt || isShift)
michael@0 461 return false;
michael@0 462
michael@0 463 keyEvent->GetKeyCode(&code);
michael@0 464 switch (code)
michael@0 465 {
michael@0 466 case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
michael@0 467 return mParams.mRepeatIterationOrAccessKey == 0x08;
michael@0 468
michael@0 469 case nsIDOMKeyEvent::DOM_VK_RETURN:
michael@0 470 return mParams.mRepeatIterationOrAccessKey == 0x0A ||
michael@0 471 mParams.mRepeatIterationOrAccessKey == 0x0D;
michael@0 472
michael@0 473 case nsIDOMKeyEvent::DOM_VK_ESCAPE:
michael@0 474 return mParams.mRepeatIterationOrAccessKey == 0x1B;
michael@0 475
michael@0 476 case nsIDOMKeyEvent::DOM_VK_DELETE:
michael@0 477 return mParams.mRepeatIterationOrAccessKey == 0x7F;
michael@0 478
michael@0 479 default:
michael@0 480 return false;
michael@0 481 }
michael@0 482 }
michael@0 483
michael@0 484 nsSMILTimeValue
michael@0 485 nsSMILTimeValueSpec::ConvertBetweenTimeContainers(
michael@0 486 const nsSMILTimeValue& aSrcTime,
michael@0 487 const nsSMILTimeContainer* aSrcContainer)
michael@0 488 {
michael@0 489 // If the source time is either indefinite or unresolved the result is going
michael@0 490 // to be the same
michael@0 491 if (!aSrcTime.IsDefinite())
michael@0 492 return aSrcTime;
michael@0 493
michael@0 494 // Convert from source time container to our parent time container
michael@0 495 const nsSMILTimeContainer* dstContainer = mOwner->GetTimeContainer();
michael@0 496 if (dstContainer == aSrcContainer)
michael@0 497 return aSrcTime;
michael@0 498
michael@0 499 // If one of the elements is not attached to a time container then we can't do
michael@0 500 // any meaningful conversion
michael@0 501 if (!aSrcContainer || !dstContainer)
michael@0 502 return nsSMILTimeValue(); // unresolved
michael@0 503
michael@0 504 nsSMILTimeValue docTime =
michael@0 505 aSrcContainer->ContainerToParentTime(aSrcTime.GetMillis());
michael@0 506
michael@0 507 if (docTime.IsIndefinite())
michael@0 508 // This will happen if the source container is paused and we have a future
michael@0 509 // time. Just return the indefinite time.
michael@0 510 return docTime;
michael@0 511
michael@0 512 NS_ABORT_IF_FALSE(docTime.IsDefinite(),
michael@0 513 "ContainerToParentTime gave us an unresolved or indefinite time");
michael@0 514
michael@0 515 return dstContainer->ParentToContainerTime(docTime.GetMillis());
michael@0 516 }
michael@0 517
michael@0 518 bool
michael@0 519 nsSMILTimeValueSpec::ApplyOffset(nsSMILTimeValue& aTime) const
michael@0 520 {
michael@0 521 // indefinite + offset = indefinite. Likewise for unresolved times.
michael@0 522 if (!aTime.IsDefinite()) {
michael@0 523 return true;
michael@0 524 }
michael@0 525
michael@0 526 double resultAsDouble =
michael@0 527 (double)aTime.GetMillis() + mParams.mOffset.GetMillis();
michael@0 528 if (resultAsDouble > std::numeric_limits<nsSMILTime>::max() ||
michael@0 529 resultAsDouble < std::numeric_limits<nsSMILTime>::min()) {
michael@0 530 return false;
michael@0 531 }
michael@0 532 aTime.SetMillis(aTime.GetMillis() + mParams.mOffset.GetMillis());
michael@0 533 return true;
michael@0 534 }

mercurial