Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 | } |