68.13.9 - dom

This commit is contained in:
Fedor 2024-02-16 13:16:24 +02:00
parent 45b510c8d7
commit 40abca8e41
1386 changed files with 24875 additions and 17201 deletions

View File

@ -17,6 +17,7 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/DeclarationBlock.h"
#include "mozilla/Maybe.h" // For Maybe
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/TypeTraits.h" // For std::forward<>
#include "nsAnimationManager.h" // For CSSAnimation
#include "nsComputedDOMStyle.h"
@ -60,13 +61,13 @@ class MOZ_RAII AutoMutationBatchForAnimation {
explicit AutoMutationBatchForAnimation(
const Animation& aAnimation MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
Maybe<NonOwningAnimationTarget> target = aAnimation.GetTargetForAnimation();
NonOwningAnimationTarget target = aAnimation.GetTargetForAnimation();
if (!target) {
return;
}
// For mutation observers, we use the OwnerDoc.
mAutoBatch.emplace(target->mElement->OwnerDoc());
mAutoBatch.emplace(target.mElement->OwnerDoc());
}
private:
@ -81,12 +82,13 @@ class MOZ_RAII AutoMutationBatchForAnimation {
//
// ---------------------------------------------------------------------------
Maybe<NonOwningAnimationTarget> Animation::GetTargetForAnimation() const {
NonOwningAnimationTarget Animation::GetTargetForAnimation() const {
AnimationEffect* effect = GetEffect();
NonOwningAnimationTarget target;
if (!effect || !effect->AsKeyframeEffect()) {
return Nothing();
return target;
}
return effect->AsKeyframeEffect()->GetTarget();
return effect->AsKeyframeEffect()->GetAnimationTarget();
}
/* static */
@ -363,9 +365,15 @@ void Animation::UpdatePlaybackRate(double aPlaybackRate) {
mPendingPlaybackRate = Some(aPlaybackRate);
// If we already have a pending task, there is nothing more to do since the
// playback rate will be applied then.
if (Pending()) {
// If we already have a pending task, there is nothing more to do since the
// playback rate will be applied then.
//
// However, as with the idle/paused case below, we still need to update the
// relevance (and effect set to make sure it only contains relevant
// animations) since the relevance is based on the Animation play state
// which incorporates the _pending_ playback rate.
UpdateEffect(PostRestyleMode::Never);
return;
}
@ -387,8 +395,9 @@ void Animation::UpdatePlaybackRate(double aPlaybackRate) {
// moving. Once we get a start time etc. we'll update the playback rate
// then.
//
// All we need to do is update observers so that, e.g. DevTools, report the
// right information.
// However we still need to update the relevance and effect set as well as
// notifying observers.
UpdateEffect(PostRestyleMode::Never);
if (IsRelevant()) {
MutationObservers::NotifyAnimationChanged(this);
}
@ -639,18 +648,18 @@ void Animation::CommitStyles(ErrorResult& aRv) {
return;
}
Maybe<NonOwningAnimationTarget> target = keyframeEffect->GetTarget();
NonOwningAnimationTarget target = keyframeEffect->GetAnimationTarget();
if (!target) {
return;
}
if (target->mPseudoType != PseudoStyleType::NotPseudo) {
if (target.mPseudoType != PseudoStyleType::NotPseudo) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
}
// Check it is an element with a style attribute
nsCOMPtr<nsStyledElement> styledElement = do_QueryInterface(target->mElement);
nsCOMPtr<nsStyledElement> styledElement = do_QueryInterface(target.mElement);
if (!styledElement) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
@ -658,16 +667,16 @@ void Animation::CommitStyles(ErrorResult& aRv) {
// Flush style before checking if the target element is rendered since the
// result could depend on pending style changes.
if (Document* doc = target->mElement->GetComposedDoc()) {
if (Document* doc = target.mElement->GetComposedDoc()) {
doc->FlushPendingNotifications(FlushType::Style);
}
if (!target->mElement->IsRendered()) {
if (!target.mElement->IsRendered()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsPresContext* presContext =
nsContentUtils::GetContextForContent(target->mElement);
nsContentUtils::GetContextForContent(target.mElement);
if (!presContext) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
@ -686,11 +695,11 @@ void Animation::CommitStyles(ErrorResult& aRv) {
// Start the update now so that the old rule doesn't get used
// between when we mutate the declaration and when we set the new
// rule.
mozAutoDocUpdate autoUpdate(target->mElement->OwnerDoc(), true);
mozAutoDocUpdate autoUpdate(target.mElement->OwnerDoc(), true);
// Get the inline style to append to
RefPtr<DeclarationBlock> declarationBlock;
if (auto* existing = target->mElement->GetInlineStyleDeclaration()) {
if (auto* existing = target.mElement->GetInlineStyleDeclaration()) {
declarationBlock = existing->EnsureMutable();
} else {
declarationBlock = new DeclarationBlock();
@ -700,7 +709,7 @@ void Animation::CommitStyles(ErrorResult& aRv) {
// Prepare the callback
MutationClosureData closureData;
closureData.mClosure = nsDOMCSSAttributeDeclaration::MutationClosureFunction;
closureData.mElement = target->mElement;
closureData.mElement = target.mElement;
DeclarationBlockMutationClosure beforeChangeClosure = {
nsDOMCSSAttributeDeclaration::MutationClosureFunction,
&closureData,
@ -724,7 +733,7 @@ void Animation::CommitStyles(ErrorResult& aRv) {
}
// Update inline style declaration
target->mElement->SetInlineStyleDeclaration(*declarationBlock, closureData);
target.mElement->SetInlineStyleDeclaration(*declarationBlock, closureData);
}
// ---------------------------------------------------------------------------
@ -749,7 +758,9 @@ void Animation::SetCurrentTimeAsDouble(const Nullable<double>& aCurrentTime,
ErrorResult& aRv) {
if (aCurrentTime.IsNull()) {
if (!GetCurrentTimeAsDuration().IsNull()) {
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
aRv.ThrowTypeError(
"Current time is resolved but trying to set it to an unresolved "
"time");
}
return;
}
@ -963,7 +974,9 @@ bool Animation::ShouldBeSynchronizedWithMainThread(
// We check this before calling ShouldBlockAsyncTransformAnimations, partly
// because it's cheaper, but also because it's often the most useful thing
// to know when you're debugging performance.
if (mSyncWithGeometricAnimations &&
if (StaticPrefs::
dom_animations_mainthread_synchronization_with_geometric_animations() &&
mSyncWithGeometricAnimations &&
keyframeEffect->HasAnimationOfPropertySet(
nsCSSPropertyIDSet::TransformLikeProperties())) {
aPerformanceWarning =
@ -1048,7 +1061,7 @@ bool Animation::IsReplaceable() const {
// We should only replace animations with a target element (since otherwise
// what other effects would we consider when determining if they are covered
// or not?).
if (!GetEffect()->AsKeyframeEffect()->GetTarget()) {
if (!GetEffect()->AsKeyframeEffect()->GetAnimationTarget()) {
return false;
}
@ -1067,15 +1080,16 @@ void Animation::ScheduleReplacementCheck() {
// If IsReplaceable() is true, the following should also hold
MOZ_ASSERT(GetEffect());
MOZ_ASSERT(GetEffect()->AsKeyframeEffect());
MOZ_ASSERT(GetEffect()->AsKeyframeEffect()->GetTarget());
Maybe<NonOwningAnimationTarget> target =
GetEffect()->AsKeyframeEffect()->GetTarget();
NonOwningAnimationTarget target =
GetEffect()->AsKeyframeEffect()->GetAnimationTarget();
MOZ_ASSERT(target);
nsPresContext* presContext =
nsContentUtils::GetContextForContent(target->mElement);
nsContentUtils::GetContextForContent(target.mElement);
if (presContext) {
presContext->EffectCompositor()->NoteElementForReducing(*target);
presContext->EffectCompositor()->NoteElementForReducing(target);
}
}

View File

@ -46,7 +46,7 @@ class Document;
class Animation : public DOMEventTargetHelper,
public LinkedListElement<Animation> {
protected:
virtual ~Animation() {}
virtual ~Animation() = default;
public:
explicit Animation(nsIGlobalObject* aGlobal)
@ -61,7 +61,7 @@ class Animation : public DOMEventTargetHelper,
* Utility function to get the target (pseudo-)element associated with an
* animation.
*/
Maybe<NonOwningAnimationTarget> GetTargetForAnimation() const;
NonOwningAnimationTarget GetTargetForAnimation() const;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -88,7 +88,7 @@ class Animation : public DOMEventTargetHelper,
void SetId(const nsAString& aId);
AnimationEffect* GetEffect() const { return mEffect; }
void SetEffect(AnimationEffect* aEffect);
virtual void SetEffect(AnimationEffect* aEffect);
void SetEffectNoUpdate(AnimationEffect* aEffect);
AnimationTimeline* GetTimeline() const { return mTimeline; }
@ -98,7 +98,7 @@ class Animation : public DOMEventTargetHelper,
Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
Nullable<double> GetStartTimeAsDouble() const;
void SetStartTime(const Nullable<TimeDuration>& aNewStartTime);
void SetStartTimeAsDouble(const Nullable<double>& aStartTime);
virtual void SetStartTimeAsDouble(const Nullable<double>& aStartTime);
// This is deliberately _not_ called GetCurrentTime since that would clash
// with a macro defined in winbase.h
@ -131,21 +131,16 @@ class Animation : public DOMEventTargetHelper,
void Finish(ErrorResult& aRv);
virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior);
void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior);
virtual void PlayFromJS(ErrorResult& aRv) {
Play(aRv, LimitBehavior::AutoRewind);
}
virtual void Pause(ErrorResult& aRv);
/**
* PauseFromJS is currently only here for symmetry with PlayFromJS but
* in future we will likely have to flush style in
* CSSAnimation::PauseFromJS so we leave it for now.
*/
void PauseFromJS(ErrorResult& aRv) { Pause(aRv); }
void Pause(ErrorResult& aRv);
virtual void PauseFromJS(ErrorResult& aRv) { Pause(aRv); }
void UpdatePlaybackRate(double aPlaybackRate);
void Reverse(ErrorResult& aRv);
virtual void Reverse(ErrorResult& aRv);
void Persist();
void CommitStyles(ErrorResult& aRv);

View File

@ -10,6 +10,7 @@
#include "mozilla/dom/MutationObservers.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/FloatingPoint.h"
#include "nsDOMMutationObserver.h"
namespace mozilla {
namespace dom {
@ -71,8 +72,8 @@ void AnimationEffect::SetSpecifiedTiming(TimingParams&& aTiming) {
if (mAnimation) {
Maybe<nsAutoAnimationMutationBatch> mb;
if (AsKeyframeEffect() && AsKeyframeEffect()->GetTarget()) {
mb.emplace(AsKeyframeEffect()->GetTarget()->mElement->OwnerDoc());
if (AsKeyframeEffect() && AsKeyframeEffect()->GetAnimationTarget()) {
mb.emplace(AsKeyframeEffect()->GetAnimationTarget().mElement->OwnerDoc());
}
mAnimation->NotifyEffectTimingUpdated();

View File

@ -16,9 +16,6 @@
#include "nsWrapperCache.h"
namespace mozilla {
struct ElementPropertyTransition;
namespace dom {
class Animation;
@ -35,11 +32,6 @@ class AnimationEffect : public nsISupports, public nsWrapperCache {
virtual KeyframeEffect* AsKeyframeEffect() { return nullptr; }
virtual ElementPropertyTransition* AsTransition() { return nullptr; }
virtual const ElementPropertyTransition* AsTransition() const {
return nullptr;
}
nsISupports* GetParentObject() const { return ToSupports(mDocument); }
bool IsCurrent() const;
@ -49,9 +41,10 @@ class AnimationEffect : public nsISupports, public nsWrapperCache {
}
// AnimationEffect interface
void GetTiming(EffectTiming& aRetVal) const;
void GetComputedTimingAsDict(ComputedEffectTiming& aRetVal) const;
void UpdateTiming(const OptionalEffectTiming& aTiming, ErrorResult& aRv);
virtual void GetTiming(EffectTiming& aRetVal) const;
virtual void GetComputedTimingAsDict(ComputedEffectTiming& aRetVal) const;
virtual void UpdateTiming(const OptionalEffectTiming& aTiming,
ErrorResult& aRv);
const TimingParams& SpecifiedTiming() const { return mTiming; }
void SetSpecifiedTiming(TimingParams&& aTiming);

View File

@ -5,6 +5,7 @@
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/EventDispatcher.h"
#include "nsPresContext.h"
#include "nsRefreshDriver.h"
namespace mozilla {

View File

@ -66,6 +66,9 @@ bool AnimationPerformanceWarning::ToLocalizedString(
case Type::HasRenderingObserver:
key = "CompositorAnimationWarningHasRenderingObserver";
break;
case Type::HasCurrentColor:
key = "CompositorAnimationWarningHasCurrentColor";
break;
case Type::None:
MOZ_ASSERT_UNREACHABLE("Uninitialized type shouldn't be used");
return false;

View File

@ -28,6 +28,7 @@ struct AnimationPerformanceWarning {
TransformIsBlockedByImportantRules,
OpacityFrameInactive,
HasRenderingObserver,
HasCurrentColor,
};
explicit AnimationPerformanceWarning(Type aType) : mType(aType) {

View File

@ -19,6 +19,8 @@ class Element;
} // namespace dom
struct OwningAnimationTarget {
OwningAnimationTarget() = default;
OwningAnimationTarget(dom::Element* aElement, PseudoStyleType aType)
: mElement(aElement), mPseudoType(aType) {}
@ -28,6 +30,8 @@ struct OwningAnimationTarget {
return mElement == aOther.mElement && mPseudoType == aOther.mPseudoType;
}
explicit operator bool() const { return !!mElement; }
// mElement represents the parent element of a pseudo-element, not the
// generated content element.
RefPtr<dom::Element> mElement;
@ -47,6 +51,14 @@ struct NonOwningAnimationTarget {
return mElement == aOther.mElement && mPseudoType == aOther.mPseudoType;
}
NonOwningAnimationTarget& operator=(const OwningAnimationTarget& aOther) {
mElement = aOther.mElement;
mPseudoType = aOther.mPseudoType;
return *this;
}
explicit operator bool() const { return !!mElement; }
// mElement represents the parent element of a pseudo-element, not the
// generated content element.
dom::Element* MOZ_NON_OWNING_REF mElement = nullptr;

View File

@ -4,10 +4,10 @@
#include "AnimationUtils.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/KeyframeEffect.h"
#include "mozilla/EffectSet.h"
#include "mozilla/Preferences.h"
#include "nsDebug.h"
#include "nsAtom.h"
#include "nsIContent.h"
@ -56,20 +56,6 @@ Document* AnimationUtils::GetDocumentFromGlobal(JSObject* aGlobalObject) {
return win->GetDoc();
}
/* static */
bool AnimationUtils::IsOffscreenThrottlingEnabled() {
static bool sOffscreenThrottlingEnabled;
static bool sPrefCached = false;
if (!sPrefCached) {
sPrefCached = true;
Preferences::AddBoolVarCache(&sOffscreenThrottlingEnabled,
"dom.animations.offscreen-throttling");
}
return sOffscreenThrottlingEnabled;
}
/* static */
bool AnimationUtils::FrameHasAnimatedScale(const nsIFrame* aFrame) {
EffectSet* effectSet = EffectSet::GetEffectSetForFrame(

View File

@ -71,11 +71,6 @@ class AnimationUtils {
*/
static Document* GetDocumentFromGlobal(JSObject* aGlobalObject);
/**
* Checks if offscreen animation throttling is enabled.
*/
static bool IsOffscreenThrottlingEnabled();
/**
* Returns true if the given frame has an animated scale.
*/

View File

@ -0,0 +1,365 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CSSAnimation.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/dom/CSSAnimationBinding.h"
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/TimeStamp.h"
#include "nsPresContext.h"
namespace mozilla {
namespace dom {
using AnimationPhase = ComputedTiming::AnimationPhase;
JSObject* CSSAnimation::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return dom::CSSAnimation_Binding::Wrap(aCx, this, aGivenProto);
}
void CSSAnimation::SetEffect(AnimationEffect* aEffect) {
Animation::SetEffect(aEffect);
AddOverriddenProperties(CSSAnimationProperties::Effect);
}
void CSSAnimation::SetStartTimeAsDouble(const Nullable<double>& aStartTime) {
// Note that we always compare with the paused state since for the purposes
// of determining if play control is being overridden or not, we want to
// treat the finished state as running.
bool wasPaused = PlayState() == AnimationPlayState::Paused;
Animation::SetStartTimeAsDouble(aStartTime);
bool isPaused = PlayState() == AnimationPlayState::Paused;
if (wasPaused != isPaused) {
AddOverriddenProperties(CSSAnimationProperties::PlayState);
}
}
mozilla::dom::Promise* CSSAnimation::GetReady(ErrorResult& aRv) {
FlushUnanimatedStyle();
return Animation::GetReady(aRv);
}
void CSSAnimation::Reverse(ErrorResult& aRv) {
// As with CSSAnimation::SetStartTimeAsDouble, we're really only interested in
// the paused state.
bool wasPaused = PlayState() == AnimationPlayState::Paused;
Animation::Reverse(aRv);
if (aRv.Failed()) {
return;
}
bool isPaused = PlayState() == AnimationPlayState::Paused;
if (wasPaused != isPaused) {
AddOverriddenProperties(CSSAnimationProperties::PlayState);
}
}
AnimationPlayState CSSAnimation::PlayStateFromJS() const {
// Flush style to ensure that any properties controlling animation state
// (e.g. animation-play-state) are fully updated.
FlushUnanimatedStyle();
return Animation::PlayStateFromJS();
}
bool CSSAnimation::PendingFromJS() const {
// Flush style since, for example, if the animation-play-state was just
// changed its possible we should now be pending.
FlushUnanimatedStyle();
return Animation::PendingFromJS();
}
void CSSAnimation::PlayFromJS(ErrorResult& aRv) {
// Note that flushing style below might trigger calls to
// PlayFromStyle()/PauseFromStyle() on this object.
FlushUnanimatedStyle();
Animation::PlayFromJS(aRv);
if (aRv.Failed()) {
return;
}
AddOverriddenProperties(CSSAnimationProperties::PlayState);
}
void CSSAnimation::PauseFromJS(ErrorResult& aRv) {
Animation::PauseFromJS(aRv);
if (aRv.Failed()) {
return;
}
AddOverriddenProperties(CSSAnimationProperties::PlayState);
}
void CSSAnimation::PlayFromStyle() {
ErrorResult rv;
Animation::Play(rv, Animation::LimitBehavior::Continue);
// play() should not throw when LimitBehavior is Continue
MOZ_ASSERT(!rv.Failed(), "Unexpected exception playing animation");
}
void CSSAnimation::PauseFromStyle() {
ErrorResult rv;
Animation::Pause(rv);
// pause() should only throw when *all* of the following conditions are true:
// - we are in the idle state, and
// - we have a negative playback rate, and
// - we have an infinitely repeating animation
// The first two conditions will never happen under regular style processing
// but could happen if an author made modifications to the Animation object
// and then updated animation-play-state. It's an unusual case and there's
// no obvious way to pass on the exception information so we just silently
// fail for now.
if (rv.Failed()) {
NS_WARNING("Unexpected exception pausing animation - silently failing");
}
}
void CSSAnimation::Tick() {
Animation::Tick();
QueueEvents();
}
bool CSSAnimation::HasLowerCompositeOrderThan(
const CSSAnimation& aOther) const {
MOZ_ASSERT(IsTiedToMarkup() && aOther.IsTiedToMarkup(),
"Should only be called for CSS animations that are sorted "
"as CSS animations (i.e. tied to CSS markup)");
// 0. Object-equality case
if (&aOther == this) {
return false;
}
// 1. Sort by document order
if (!mOwningElement.Equals(aOther.mOwningElement)) {
return mOwningElement.LessThan(
const_cast<CSSAnimation*>(this)->CachedChildIndexRef(),
aOther.mOwningElement,
const_cast<CSSAnimation*>(&aOther)->CachedChildIndexRef());
}
// 2. (Same element and pseudo): Sort by position in animation-name
return mAnimationIndex < aOther.mAnimationIndex;
}
void CSSAnimation::QueueEvents(const StickyTimeDuration& aActiveTime) {
// If the animation is pending, we ignore animation events until we finish
// pending.
if (mPendingState != PendingState::NotPending) {
return;
}
// CSS animations dispatch events at their owning element. This allows
// script to repurpose a CSS animation to target a different element,
// to use a group effect (which has no obvious "target element"), or
// to remove the animation effect altogether whilst still getting
// animation events.
//
// It does mean, however, that for a CSS animation that has no owning
// element (e.g. it was created using the CSSAnimation constructor or
// disassociated from CSS) no events are fired. If it becomes desirable
// for these animations to still fire events we should spec the concept
// of the "original owning element" or "event target" and allow script
// to set it when creating a CSSAnimation object.
if (!mOwningElement.IsSet()) {
return;
}
nsPresContext* presContext = mOwningElement.GetPresContext();
if (!presContext) {
return;
}
uint64_t currentIteration = 0;
ComputedTiming::AnimationPhase currentPhase;
StickyTimeDuration intervalStartTime;
StickyTimeDuration intervalEndTime;
StickyTimeDuration iterationStartTime;
if (!mEffect) {
currentPhase =
GetAnimationPhaseWithoutEffect<ComputedTiming::AnimationPhase>(*this);
if (currentPhase == mPreviousPhase) {
return;
}
} else {
ComputedTiming computedTiming = mEffect->GetComputedTiming();
currentPhase = computedTiming.mPhase;
currentIteration = computedTiming.mCurrentIteration;
if (currentPhase == mPreviousPhase &&
currentIteration == mPreviousIteration) {
return;
}
intervalStartTime = IntervalStartTime(computedTiming.mActiveDuration);
intervalEndTime = IntervalEndTime(computedTiming.mActiveDuration);
uint64_t iterationBoundary = mPreviousIteration > currentIteration
? currentIteration + 1
: currentIteration;
iterationStartTime = computedTiming.mDuration.MultDouble(
(iterationBoundary - computedTiming.mIterationStart));
}
TimeStamp startTimeStamp = ElapsedTimeToTimeStamp(intervalStartTime);
TimeStamp endTimeStamp = ElapsedTimeToTimeStamp(intervalEndTime);
TimeStamp iterationTimeStamp = ElapsedTimeToTimeStamp(iterationStartTime);
AutoTArray<AnimationEventInfo, 2> events;
auto appendAnimationEvent = [&](EventMessage aMessage,
const StickyTimeDuration& aElapsedTime,
const TimeStamp& aScheduledEventTimeStamp) {
double elapsedTime = aElapsedTime.ToSeconds();
if (aMessage == eAnimationCancel) {
// 0 is an inappropriate value for this callsite. What we need to do is
// use a single random value for all increasing times reportable.
// That is to say, whenever elapsedTime goes negative (because an
// animation restarts, something rewinds the animation, or otherwise)
// a new random value for the mix-in must be generated.
elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(
elapsedTime, 0, TimerPrecisionType::RFPOnly);
}
events.AppendElement(
AnimationEventInfo(mAnimationName, mOwningElement.Target(), aMessage,
elapsedTime, aScheduledEventTimeStamp, this));
};
// Handle cancel event first
if ((mPreviousPhase != AnimationPhase::Idle &&
mPreviousPhase != AnimationPhase::After) &&
currentPhase == AnimationPhase::Idle) {
appendAnimationEvent(eAnimationCancel, aActiveTime,
GetTimelineCurrentTimeAsTimeStamp());
}
switch (mPreviousPhase) {
case AnimationPhase::Idle:
case AnimationPhase::Before:
if (currentPhase == AnimationPhase::Active) {
appendAnimationEvent(eAnimationStart, intervalStartTime,
startTimeStamp);
} else if (currentPhase == AnimationPhase::After) {
appendAnimationEvent(eAnimationStart, intervalStartTime,
startTimeStamp);
appendAnimationEvent(eAnimationEnd, intervalEndTime, endTimeStamp);
}
break;
case AnimationPhase::Active:
if (currentPhase == AnimationPhase::Before) {
appendAnimationEvent(eAnimationEnd, intervalStartTime, startTimeStamp);
} else if (currentPhase == AnimationPhase::Active) {
// The currentIteration must have changed or element we would have
// returned early above.
MOZ_ASSERT(currentIteration != mPreviousIteration);
appendAnimationEvent(eAnimationIteration, iterationStartTime,
iterationTimeStamp);
} else if (currentPhase == AnimationPhase::After) {
appendAnimationEvent(eAnimationEnd, intervalEndTime, endTimeStamp);
}
break;
case AnimationPhase::After:
if (currentPhase == AnimationPhase::Before) {
appendAnimationEvent(eAnimationStart, intervalEndTime, startTimeStamp);
appendAnimationEvent(eAnimationEnd, intervalStartTime, endTimeStamp);
} else if (currentPhase == AnimationPhase::Active) {
appendAnimationEvent(eAnimationStart, intervalEndTime, endTimeStamp);
}
break;
}
mPreviousPhase = currentPhase;
mPreviousIteration = currentIteration;
if (!events.IsEmpty()) {
presContext->AnimationEventDispatcher()->QueueEvents(std::move(events));
}
}
void CSSAnimation::UpdateTiming(SeekFlag aSeekFlag,
SyncNotifyFlag aSyncNotifyFlag) {
if (mNeedsNewAnimationIndexWhenRun &&
PlayState() != AnimationPlayState::Idle) {
mAnimationIndex = sNextAnimationIndex++;
mNeedsNewAnimationIndexWhenRun = false;
}
Animation::UpdateTiming(aSeekFlag, aSyncNotifyFlag);
}
/////////////////////// CSSAnimationKeyframeEffect ////////////////////////
void CSSAnimationKeyframeEffect::GetTiming(EffectTiming& aRetVal) const {
MaybeFlushUnanimatedStyle();
KeyframeEffect::GetTiming(aRetVal);
}
void CSSAnimationKeyframeEffect::GetComputedTimingAsDict(
ComputedEffectTiming& aRetVal) const {
MaybeFlushUnanimatedStyle();
KeyframeEffect::GetComputedTimingAsDict(aRetVal);
}
void CSSAnimationKeyframeEffect::UpdateTiming(
const OptionalEffectTiming& aTiming, ErrorResult& aRv) {
KeyframeEffect::UpdateTiming(aTiming, aRv);
if (aRv.Failed()) {
return;
}
if (CSSAnimation* cssAnimation = GetOwningCSSAnimation()) {
CSSAnimationProperties updatedProperties = CSSAnimationProperties::None;
if (aTiming.mDuration.WasPassed()) {
updatedProperties |= CSSAnimationProperties::Duration;
}
if (aTiming.mIterations.WasPassed()) {
updatedProperties |= CSSAnimationProperties::IterationCount;
}
if (aTiming.mDirection.WasPassed()) {
updatedProperties |= CSSAnimationProperties::Direction;
}
if (aTiming.mDelay.WasPassed()) {
updatedProperties |= CSSAnimationProperties::Delay;
}
if (aTiming.mFill.WasPassed()) {
updatedProperties |= CSSAnimationProperties::FillMode;
}
cssAnimation->AddOverriddenProperties(updatedProperties);
}
}
void CSSAnimationKeyframeEffect::SetKeyframes(JSContext* aContext,
JS::Handle<JSObject*> aKeyframes,
ErrorResult& aRv) {
KeyframeEffect::SetKeyframes(aContext, aKeyframes, aRv);
if (aRv.Failed()) {
return;
}
if (CSSAnimation* cssAnimation = GetOwningCSSAnimation()) {
cssAnimation->AddOverriddenProperties(CSSAnimationProperties::Keyframes);
}
}
void CSSAnimationKeyframeEffect::MaybeFlushUnanimatedStyle() const {
if (!GetOwningCSSAnimation()) {
return;
}
if (dom::Document* doc = GetRenderedDocument()) {
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
}
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,247 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_CSSAnimation_h
#define mozilla_dom_CSSAnimation_h
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/KeyframeEffect.h"
#include "mozilla/dom/MutationObservers.h"
#include "mozilla/StyleAnimationValue.h"
#include "AnimationCommon.h"
namespace mozilla {
// Properties of CSS Animations that can be overridden by the Web Animations API
// in a manner that means we should ignore subsequent changes to markup for that
// property.
enum class CSSAnimationProperties {
None = 0,
Keyframes = 1 << 0,
Duration = 1 << 1,
IterationCount = 1 << 2,
Direction = 1 << 3,
Delay = 1 << 4,
FillMode = 1 << 5,
Effect = Keyframes | Duration | IterationCount | Direction | Delay | FillMode,
PlayState = 1 << 6,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSAnimationProperties)
namespace dom {
class CSSAnimation final : public Animation {
public:
explicit CSSAnimation(nsIGlobalObject* aGlobal, nsAtom* aAnimationName)
: dom::Animation(aGlobal),
mAnimationName(aAnimationName),
mNeedsNewAnimationIndexWhenRun(false),
mPreviousPhase(ComputedTiming::AnimationPhase::Idle),
mPreviousIteration(0) {
// We might need to drop this assertion once we add a script-accessible
// constructor but for animations generated from CSS markup the
// animation-name should never be empty.
MOZ_ASSERT(mAnimationName != nsGkAtoms::_empty,
"animation-name should not be 'none'");
}
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
CSSAnimation* AsCSSAnimation() override { return this; }
const CSSAnimation* AsCSSAnimation() const override { return this; }
// CSSAnimation interface
void GetAnimationName(nsString& aRetVal) const {
mAnimationName->ToString(aRetVal);
}
nsAtom* AnimationName() const { return mAnimationName; }
// Animation interface overrides
void SetEffect(AnimationEffect* aEffect) override;
void SetStartTimeAsDouble(const Nullable<double>& aStartTime) override;
Promise* GetReady(ErrorResult& aRv) override;
void Reverse(ErrorResult& aRv) override;
// NOTE: tabbrowser.xml currently relies on the fact that reading the
// currentTime of a CSSAnimation does *not* flush style (whereas reading the
// playState does). If CSS Animations 2 specifies that reading currentTime
// also flushes style we will need to find another way to detect canceled
// animations in tabbrowser.xml. On the other hand, if CSS Animations 2
// specifies that reading playState does *not* flush style (and we drop the
// following override), then we should update tabbrowser.xml to check
// the playState instead.
AnimationPlayState PlayStateFromJS() const override;
bool PendingFromJS() const override;
void PlayFromJS(ErrorResult& aRv) override;
void PauseFromJS(ErrorResult& aRv) override;
void PlayFromStyle();
void PauseFromStyle();
void CancelFromStyle(PostRestyleMode aPostRestyle) {
// When an animation is disassociated with style it enters an odd state
// where its composite order is undefined until it first transitions
// out of the idle state.
//
// Even if the composite order isn't defined we don't want it to be random
// in case we need to determine the order to dispatch events associated
// with an animation in this state. To solve this we treat the animation as
// if it had been added to the end of the global animation list so that
// its sort order is defined. We'll update this index again once the
// animation leaves the idle state.
mAnimationIndex = sNextAnimationIndex++;
mNeedsNewAnimationIndexWhenRun = true;
Animation::Cancel(aPostRestyle);
// We need to do this *after* calling Cancel() since
// Cancel() might synchronously trigger a cancel event for which
// we need an owning element to target the event at.
mOwningElement = OwningElementRef();
}
void Tick() override;
void QueueEvents(
const StickyTimeDuration& aActiveTime = StickyTimeDuration());
bool HasLowerCompositeOrderThan(const CSSAnimation& aOther) const;
void SetAnimationIndex(uint64_t aIndex) {
MOZ_ASSERT(IsTiedToMarkup());
if (IsRelevant() && mAnimationIndex != aIndex) {
MutationObservers::NotifyAnimationChanged(this);
PostUpdate();
}
mAnimationIndex = aIndex;
}
// Sets the owning element which is used for determining the composite
// order of CSSAnimation objects generated from CSS markup.
//
// @see mOwningElement
void SetOwningElement(const OwningElementRef& aElement) {
mOwningElement = aElement;
}
// True for animations that are generated from CSS markup and continue to
// reflect changes to that markup.
bool IsTiedToMarkup() const { return mOwningElement.IsSet(); }
void MaybeQueueCancelEvent(const StickyTimeDuration& aActiveTime) override {
QueueEvents(aActiveTime);
}
CSSAnimationProperties GetOverriddenProperties() const {
return mOverriddenProperties;
}
void AddOverriddenProperties(CSSAnimationProperties aProperties) {
mOverriddenProperties |= aProperties;
}
protected:
virtual ~CSSAnimation() {
MOZ_ASSERT(!mOwningElement.IsSet(),
"Owning element should be cleared "
"before a CSS animation is destroyed");
}
// Animation overrides
void UpdateTiming(SeekFlag aSeekFlag,
SyncNotifyFlag aSyncNotifyFlag) override;
// Returns the duration from the start of the animation's source effect's
// active interval to the point where the animation actually begins playback.
// This is zero unless the animation's source effect has a negative delay in
// which case it is the absolute value of that delay.
// This is used for setting the elapsedTime member of CSS AnimationEvents.
TimeDuration InitialAdvance() const {
return mEffect ? std::max(TimeDuration(),
mEffect->SpecifiedTiming().Delay() * -1)
: TimeDuration();
}
RefPtr<nsAtom> mAnimationName;
// The (pseudo-)element whose computed animation-name refers to this
// animation (if any).
//
// This is used for determining the relative composite order of animations
// generated from CSS markup.
//
// Typically this will be the same as the target element of the keyframe
// effect associated with this animation. However, it can differ in the
// following circumstances:
//
// a) If script removes or replaces the effect of this animation,
// b) If this animation is cancelled (e.g. by updating the
// animation-name property or removing the owning element from the
// document),
// c) If this object is generated from script using the CSSAnimation
// constructor.
//
// For (b) and (c) the owning element will return !IsSet().
OwningElementRef mOwningElement;
// When true, indicates that when this animation next leaves the idle state,
// its animation index should be updated.
bool mNeedsNewAnimationIndexWhenRun;
// Phase and current iteration from the previous time we queued events.
// This is used to determine what new events to dispatch.
ComputedTiming::AnimationPhase mPreviousPhase;
uint64_t mPreviousIteration;
// Properties that would normally be defined by the cascade but which have
// since been explicitly set via the Web Animations API.
CSSAnimationProperties mOverriddenProperties = CSSAnimationProperties::None;
};
// A subclass of KeyframeEffect that reports when specific properties have been
// overridden via the Web Animations API.
class CSSAnimationKeyframeEffect : public KeyframeEffect {
public:
CSSAnimationKeyframeEffect(Document* aDocument,
OwningAnimationTarget&& aTarget,
TimingParams&& aTiming,
const KeyframeEffectParams& aOptions)
: KeyframeEffect(aDocument, std::move(aTarget), std::move(aTiming),
aOptions) {}
void GetTiming(EffectTiming& aRetVal) const override;
void GetComputedTimingAsDict(ComputedEffectTiming& aRetVal) const override;
void UpdateTiming(const OptionalEffectTiming& aTiming,
ErrorResult& aRv) override;
void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
ErrorResult& aRv) override;
private:
CSSAnimation* GetOwningCSSAnimation() {
return mAnimation ? mAnimation->AsCSSAnimation() : nullptr;
}
const CSSAnimation* GetOwningCSSAnimation() const {
return mAnimation ? mAnimation->AsCSSAnimation() : nullptr;
}
// Flushes styles if our owning animation is a CSSAnimation
void MaybeFlushUnanimatedStyle() const;
};
} // namespace dom
template <>
struct AnimationTypeTraits<dom::CSSAnimation> {
static nsAtom* ElementPropertyAtom() { return nsGkAtoms::animationsProperty; }
static nsAtom* BeforePropertyAtom() {
return nsGkAtoms::animationsOfBeforeProperty;
}
static nsAtom* AfterPropertyAtom() {
return nsGkAtoms::animationsOfAfterProperty;
}
static nsAtom* MarkerPropertyAtom() {
return nsGkAtoms::animationsOfMarkerProperty;
}
};
} // namespace mozilla
#endif // mozilla_dom_CSSAnimation_h

View File

@ -29,7 +29,7 @@ CSSPseudoElement::CSSPseudoElement(dom::Element* aElement,
CSSPseudoElement::~CSSPseudoElement() {
// Element might have been unlinked already, so we have to do null check.
if (mOriginatingElement) {
mOriginatingElement->DeleteProperty(
mOriginatingElement->RemoveProperty(
GetCSSPseudoElementPropertyAtom(mPseudoType));
}
}
@ -43,31 +43,6 @@ JSObject* CSSPseudoElement::WrapObject(JSContext* aCx,
return CSSPseudoElement_Binding::Wrap(aCx, this, aGivenProto);
}
void CSSPseudoElement::GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aRetVal) {
Document* doc = mOriginatingElement->GetComposedDoc();
if (doc) {
// We don't need to explicitly flush throttled animations here, since
// updating the animation style of (pseudo-)elements will never affect the
// set of running animations and it's only the set of running animations
// that is important here.
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
}
Element::GetAnimationsUnsorted(mOriginatingElement, mPseudoType, aRetVal);
aRetVal.Sort(AnimationPtrComparator<RefPtr<Animation>>());
}
already_AddRefed<Animation> CSSPseudoElement::Animate(
JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError) {
Nullable<ElementOrCSSPseudoElement> target;
target.SetValue().SetAsCSSPseudoElement() = this;
return Element::Animate(target, aContext, aKeyframes, aOptions, aError);
}
/* static */
already_AddRefed<CSSPseudoElement> CSSPseudoElement::GetCSSPseudoElement(
dom::Element* aElement, PseudoStyleType aType) {

View File

@ -52,13 +52,6 @@ class CSSPseudoElement final : public nsWrapperCache {
return retVal.forget();
}
void GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aRetVal);
already_AddRefed<Animation> Animate(
JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError);
// Given an element:pseudoType pair, returns the CSSPseudoElement stored as a
// property on |aElement|. If there is no CSSPseudoElement for the specified
// pseudo-type on element, a new CSSPseudoElement will be created and stored

View File

@ -0,0 +1,336 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CSSTransition.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/dom/CSSTransitionBinding.h"
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/KeyframeEffect.h"
#include "mozilla/TimeStamp.h"
#include "nsPresContext.h"
namespace mozilla {
namespace dom {
JSObject* CSSTransition::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return dom::CSSTransition_Binding::Wrap(aCx, this, aGivenProto);
}
void CSSTransition::GetTransitionProperty(nsString& aRetVal) const {
MOZ_ASSERT(eCSSProperty_UNKNOWN != mTransitionProperty,
"Transition Property should be initialized");
aRetVal =
NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(mTransitionProperty));
}
AnimationPlayState CSSTransition::PlayStateFromJS() const {
FlushUnanimatedStyle();
return Animation::PlayStateFromJS();
}
bool CSSTransition::PendingFromJS() const {
// Transitions don't become pending again after they start running but, if
// while the transition is still pending, style is updated in such a way
// that the transition will be canceled, we need to report false here.
// Hence we need to flush, but only when we're pending.
if (Pending()) {
FlushUnanimatedStyle();
}
return Animation::PendingFromJS();
}
void CSSTransition::PlayFromJS(ErrorResult& aRv) {
FlushUnanimatedStyle();
Animation::PlayFromJS(aRv);
}
void CSSTransition::UpdateTiming(SeekFlag aSeekFlag,
SyncNotifyFlag aSyncNotifyFlag) {
if (mNeedsNewAnimationIndexWhenRun &&
PlayState() != AnimationPlayState::Idle) {
mAnimationIndex = sNextAnimationIndex++;
mNeedsNewAnimationIndexWhenRun = false;
}
Animation::UpdateTiming(aSeekFlag, aSyncNotifyFlag);
}
void CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime) {
if (!mOwningElement.IsSet()) {
return;
}
nsPresContext* presContext = mOwningElement.GetPresContext();
if (!presContext) {
return;
}
static constexpr StickyTimeDuration zeroDuration = StickyTimeDuration();
TransitionPhase currentPhase;
StickyTimeDuration intervalStartTime;
StickyTimeDuration intervalEndTime;
if (!mEffect) {
currentPhase = GetAnimationPhaseWithoutEffect<TransitionPhase>(*this);
} else {
ComputedTiming computedTiming = mEffect->GetComputedTiming();
currentPhase = static_cast<TransitionPhase>(computedTiming.mPhase);
intervalStartTime = IntervalStartTime(computedTiming.mActiveDuration);
intervalEndTime = IntervalEndTime(computedTiming.mActiveDuration);
}
if (mPendingState != PendingState::NotPending &&
(mPreviousTransitionPhase == TransitionPhase::Idle ||
mPreviousTransitionPhase == TransitionPhase::Pending)) {
currentPhase = TransitionPhase::Pending;
}
if (currentPhase == mPreviousTransitionPhase) {
return;
}
// TimeStamps to use for ordering the events when they are dispatched. We
// use a TimeStamp so we can compare events produced by different elements,
// perhaps even with different timelines.
// The zero timestamp is for transitionrun events where we ignore the delay
// for the purpose of ordering events.
TimeStamp zeroTimeStamp = AnimationTimeToTimeStamp(zeroDuration);
TimeStamp startTimeStamp = ElapsedTimeToTimeStamp(intervalStartTime);
TimeStamp endTimeStamp = ElapsedTimeToTimeStamp(intervalEndTime);
AutoTArray<AnimationEventInfo, 3> events;
auto appendTransitionEvent = [&](EventMessage aMessage,
const StickyTimeDuration& aElapsedTime,
const TimeStamp& aScheduledEventTimeStamp) {
double elapsedTime = aElapsedTime.ToSeconds();
if (aMessage == eTransitionCancel) {
// 0 is an inappropriate value for this callsite. What we need to do is
// use a single random value for all increasing times reportable.
// That is to say, whenever elapsedTime goes negative (because an
// animation restarts, something rewinds the animation, or otherwise)
// a new random value for the mix-in must be generated.
elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(
elapsedTime, 0, TimerPrecisionType::RFPOnly);
}
events.AppendElement(AnimationEventInfo(
TransitionProperty(), mOwningElement.Target(), aMessage, elapsedTime,
aScheduledEventTimeStamp, this));
};
// Handle cancel events first
if ((mPreviousTransitionPhase != TransitionPhase::Idle &&
mPreviousTransitionPhase != TransitionPhase::After) &&
currentPhase == TransitionPhase::Idle) {
appendTransitionEvent(eTransitionCancel, aActiveTime,
GetTimelineCurrentTimeAsTimeStamp());
}
// All other events
switch (mPreviousTransitionPhase) {
case TransitionPhase::Idle:
if (currentPhase == TransitionPhase::Pending ||
currentPhase == TransitionPhase::Before) {
appendTransitionEvent(eTransitionRun, intervalStartTime, zeroTimeStamp);
} else if (currentPhase == TransitionPhase::Active) {
appendTransitionEvent(eTransitionRun, intervalStartTime, zeroTimeStamp);
appendTransitionEvent(eTransitionStart, intervalStartTime,
startTimeStamp);
} else if (currentPhase == TransitionPhase::After) {
appendTransitionEvent(eTransitionRun, intervalStartTime, zeroTimeStamp);
appendTransitionEvent(eTransitionStart, intervalStartTime,
startTimeStamp);
appendTransitionEvent(eTransitionEnd, intervalEndTime, endTimeStamp);
}
break;
case TransitionPhase::Pending:
case TransitionPhase::Before:
if (currentPhase == TransitionPhase::Active) {
appendTransitionEvent(eTransitionStart, intervalStartTime,
startTimeStamp);
} else if (currentPhase == TransitionPhase::After) {
appendTransitionEvent(eTransitionStart, intervalStartTime,
startTimeStamp);
appendTransitionEvent(eTransitionEnd, intervalEndTime, endTimeStamp);
}
break;
case TransitionPhase::Active:
if (currentPhase == TransitionPhase::After) {
appendTransitionEvent(eTransitionEnd, intervalEndTime, endTimeStamp);
} else if (currentPhase == TransitionPhase::Before) {
appendTransitionEvent(eTransitionEnd, intervalStartTime,
startTimeStamp);
}
break;
case TransitionPhase::After:
if (currentPhase == TransitionPhase::Active) {
appendTransitionEvent(eTransitionStart, intervalEndTime,
startTimeStamp);
} else if (currentPhase == TransitionPhase::Before) {
appendTransitionEvent(eTransitionStart, intervalEndTime,
startTimeStamp);
appendTransitionEvent(eTransitionEnd, intervalStartTime, endTimeStamp);
}
break;
}
mPreviousTransitionPhase = currentPhase;
if (!events.IsEmpty()) {
presContext->AnimationEventDispatcher()->QueueEvents(std::move(events));
}
}
void CSSTransition::Tick() {
Animation::Tick();
QueueEvents();
}
nsCSSPropertyID CSSTransition::TransitionProperty() const {
MOZ_ASSERT(eCSSProperty_UNKNOWN != mTransitionProperty,
"Transition property should be initialized");
return mTransitionProperty;
}
AnimationValue CSSTransition::ToValue() const {
MOZ_ASSERT(!mTransitionToValue.IsNull(),
"Transition ToValue should be initialized");
return mTransitionToValue;
}
bool CSSTransition::HasLowerCompositeOrderThan(
const CSSTransition& aOther) const {
MOZ_ASSERT(IsTiedToMarkup() && aOther.IsTiedToMarkup(),
"Should only be called for CSS transitions that are sorted "
"as CSS transitions (i.e. tied to CSS markup)");
// 0. Object-equality case
if (&aOther == this) {
return false;
}
// 1. Sort by document order
if (!mOwningElement.Equals(aOther.mOwningElement)) {
return mOwningElement.LessThan(
const_cast<CSSTransition*>(this)->CachedChildIndexRef(),
aOther.mOwningElement,
const_cast<CSSTransition*>(&aOther)->CachedChildIndexRef());
}
// 2. (Same element and pseudo): Sort by transition generation
if (mAnimationIndex != aOther.mAnimationIndex) {
return mAnimationIndex < aOther.mAnimationIndex;
}
// 3. (Same transition generation): Sort by transition property
return nsCSSProps::GetStringValue(TransitionProperty()) <
nsCSSProps::GetStringValue(aOther.TransitionProperty());
}
/* static */
Nullable<TimeDuration> CSSTransition::GetCurrentTimeAt(
const AnimationTimeline& aTimeline, const TimeStamp& aBaseTime,
const TimeDuration& aStartTime, double aPlaybackRate) {
Nullable<TimeDuration> result;
Nullable<TimeDuration> timelineTime = aTimeline.ToTimelineTime(aBaseTime);
if (!timelineTime.IsNull()) {
result.SetValue(
(timelineTime.Value() - aStartTime).MultDouble(aPlaybackRate));
}
return result;
}
double CSSTransition::CurrentValuePortion() const {
if (!GetEffect()) {
return 0.0;
}
// Transitions use a fill mode of 'backwards' so GetComputedTiming will
// never return a null time progress due to being *before* the animation
// interval. However, it might be possible that we're behind on flushing
// causing us to get called *after* the animation interval. So, just in
// case, we override the fill mode to 'both' to ensure the progress
// is never null.
TimingParams timingToUse = GetEffect()->SpecifiedTiming();
timingToUse.SetFill(dom::FillMode::Both);
ComputedTiming computedTiming = GetEffect()->GetComputedTiming(&timingToUse);
if (computedTiming.mProgress.IsNull()) {
return 0.0;
}
// 'transition-timing-function' corresponds to the effect timing while
// the transition keyframes have a linear timing function so we can ignore
// them for the purposes of calculating the value portion.
return computedTiming.mProgress.Value();
}
void CSSTransition::UpdateStartValueFromReplacedTransition() {
MOZ_ASSERT(mEffect && mEffect->AsKeyframeEffect() &&
mEffect->AsKeyframeEffect()->HasAnimationOfPropertySet(
nsCSSPropertyIDSet::CompositorAnimatables()),
"Should be called for compositor-runnable transitions");
MOZ_ASSERT(mTimeline,
"Should have a timeline if we are replacing transition start "
"values");
if (!mReplacedTransition) {
return;
}
ComputedTiming computedTiming = AnimationEffect::GetComputedTimingAt(
CSSTransition::GetCurrentTimeAt(*mTimeline, TimeStamp::Now(),
mReplacedTransition->mStartTime,
mReplacedTransition->mPlaybackRate),
mReplacedTransition->mTiming, mReplacedTransition->mPlaybackRate);
if (!computedTiming.mProgress.IsNull()) {
double valuePosition = ComputedTimingFunction::GetPortion(
mReplacedTransition->mTimingFunction, computedTiming.mProgress.Value(),
computedTiming.mBeforeFlag);
const AnimationValue& replacedFrom = mReplacedTransition->mFromValue;
const AnimationValue& replacedTo = mReplacedTransition->mToValue;
AnimationValue startValue;
startValue.mServo =
Servo_AnimationValues_Interpolate(replacedFrom.mServo,
replacedTo.mServo, valuePosition)
.Consume();
mEffect->AsKeyframeEffect()->ReplaceTransitionStartValue(
std::move(startValue));
}
mReplacedTransition.reset();
}
void CSSTransition::SetEffectFromStyle(dom::AnimationEffect* aEffect) {
Animation::SetEffectNoUpdate(aEffect);
// Initialize transition property and to value.
//
// Typically this should only be called with a KeyframeEffect representing
// a simple transition, but just to be sure we check the effect has the
// expected shape first.
const KeyframeEffect* keyframeEffect = aEffect->AsKeyframeEffect();
if (MOZ_LIKELY(keyframeEffect && keyframeEffect->Properties().Length() == 1 &&
keyframeEffect->Properties()[0].mSegments.Length() == 1)) {
mTransitionProperty = keyframeEffect->Properties()[0].mProperty;
mTransitionToValue = keyframeEffect->Properties()[0].mSegments[0].mToValue;
} else {
MOZ_ASSERT_UNREACHABLE("Transition effect has unexpected shape");
}
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,243 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_CSSTransition_h
#define mozilla_dom_CSSTransition_h
#include "mozilla/dom/Animation.h"
#include "mozilla/StyleAnimationValue.h"
#include "AnimationCommon.h"
class nsIGlobalObject;
namespace mozilla {
namespace dom {
class CSSTransition final : public Animation {
public:
explicit CSSTransition(nsIGlobalObject* aGlobal)
: dom::Animation(aGlobal),
mPreviousTransitionPhase(TransitionPhase::Idle),
mNeedsNewAnimationIndexWhenRun(false),
mTransitionProperty(eCSSProperty_UNKNOWN) {}
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
CSSTransition* AsCSSTransition() override { return this; }
const CSSTransition* AsCSSTransition() const override { return this; }
// CSSTransition interface
void GetTransitionProperty(nsString& aRetVal) const;
// Animation interface overrides
AnimationPlayState PlayStateFromJS() const override;
bool PendingFromJS() const override;
void PlayFromJS(ErrorResult& aRv) override;
// A variant of Play() that avoids posting style updates since this method
// is expected to be called whilst already updating style.
void PlayFromStyle() {
ErrorResult rv;
PlayNoUpdate(rv, Animation::LimitBehavior::Continue);
// play() should not throw when LimitBehavior is Continue
MOZ_ASSERT(!rv.Failed(), "Unexpected exception playing transition");
}
void CancelFromStyle(PostRestyleMode aPostRestyle) {
// The animation index to use for compositing will be established when
// this transition next transitions out of the idle state but we still
// update it now so that the sort order of this transition remains
// defined until that moment.
//
// See longer explanation in CSSAnimation::CancelFromStyle.
mAnimationIndex = sNextAnimationIndex++;
mNeedsNewAnimationIndexWhenRun = true;
Animation::Cancel(aPostRestyle);
// It is important we do this *after* calling Cancel().
// This is because Cancel() will end up posting a restyle and
// that restyle should target the *transitions* level of the cascade.
// However, once we clear the owning element, CascadeLevel() will begin
// returning CascadeLevel::Animations.
mOwningElement = OwningElementRef();
}
void SetEffectFromStyle(AnimationEffect* aEffect);
void Tick() override;
nsCSSPropertyID TransitionProperty() const;
AnimationValue ToValue() const;
bool HasLowerCompositeOrderThan(const CSSTransition& aOther) const;
EffectCompositor::CascadeLevel CascadeLevel() const override {
return IsTiedToMarkup() ? EffectCompositor::CascadeLevel::Transitions
: EffectCompositor::CascadeLevel::Animations;
}
void SetCreationSequence(uint64_t aIndex) {
MOZ_ASSERT(IsTiedToMarkup());
mAnimationIndex = aIndex;
}
// Sets the owning element which is used for determining the composite
// oder of CSSTransition objects generated from CSS markup.
//
// @see mOwningElement
void SetOwningElement(const OwningElementRef& aElement) {
mOwningElement = aElement;
}
// True for transitions that are generated from CSS markup and continue to
// reflect changes to that markup.
bool IsTiedToMarkup() const { return mOwningElement.IsSet(); }
// Return the animation current time based on a given TimeStamp, a given
// start time and a given playbackRate on a given timeline. This is useful
// when we estimate the current animated value running on the compositor
// because the animation on the compositor may be running ahead while
// main-thread is busy.
static Nullable<TimeDuration> GetCurrentTimeAt(
const AnimationTimeline& aTimeline, const TimeStamp& aBaseTime,
const TimeDuration& aStartTime, double aPlaybackRate);
void MaybeQueueCancelEvent(const StickyTimeDuration& aActiveTime) override {
QueueEvents(aActiveTime);
}
// Compute the portion of the *value* space that we should be through
// at the current time. (The input to the transition timing function
// has time units, the output has value units.)
double CurrentValuePortion() const;
const AnimationValue& StartForReversingTest() const {
return mStartForReversingTest;
}
double ReversePortion() const { return mReversePortion; }
void SetReverseParameters(AnimationValue&& aStartForReversingTest,
double aReversePortion) {
mStartForReversingTest = std::move(aStartForReversingTest);
mReversePortion = aReversePortion;
}
struct ReplacedTransitionProperties {
TimeDuration mStartTime;
double mPlaybackRate;
TimingParams mTiming;
Maybe<ComputedTimingFunction> mTimingFunction;
AnimationValue mFromValue, mToValue;
};
void SetReplacedTransition(
ReplacedTransitionProperties&& aReplacedTransition) {
mReplacedTransition.emplace(std::move(aReplacedTransition));
}
// For a new transition interrupting an existing transition on the
// compositor, update the start value to match the value of the replaced
// transitions at the current time.
void UpdateStartValueFromReplacedTransition();
protected:
virtual ~CSSTransition() {
MOZ_ASSERT(!mOwningElement.IsSet(),
"Owning element should be cleared "
"before a CSS transition is destroyed");
}
// Animation overrides
void UpdateTiming(SeekFlag aSeekFlag,
SyncNotifyFlag aSyncNotifyFlag) override;
void QueueEvents(const StickyTimeDuration& activeTime = StickyTimeDuration());
enum class TransitionPhase;
// The (pseudo-)element whose computed transition-property refers to this
// transition (if any).
//
// This is used for determining the relative composite order of transitions
// generated from CSS markup.
//
// Typically this will be the same as the target element of the keyframe
// effect associated with this transition. However, it can differ in the
// following circumstances:
//
// a) If script removes or replaces the effect of this transition,
// b) If this transition is cancelled (e.g. by updating the
// transition-property or removing the owning element from the document),
// c) If this object is generated from script using the CSSTransition
// constructor.
//
// For (b) and (c) the owning element will return !IsSet().
OwningElementRef mOwningElement;
// The 'transition phase' used to determine which transition events need
// to be queued on this tick.
// See: https://drafts.csswg.org/css-transitions-2/#transition-phase
enum class TransitionPhase {
Idle = static_cast<int>(ComputedTiming::AnimationPhase::Idle),
Before = static_cast<int>(ComputedTiming::AnimationPhase::Before),
Active = static_cast<int>(ComputedTiming::AnimationPhase::Active),
After = static_cast<int>(ComputedTiming::AnimationPhase::After),
Pending
};
TransitionPhase mPreviousTransitionPhase;
// When true, indicates that when this transition next leaves the idle state,
// its animation index should be updated.
bool mNeedsNewAnimationIndexWhenRun;
// Store the transition property and to-value here since we need that
// information in order to determine if there is an existing transition
// for a given style change. We can't store that information on the
// effect however since it can be replaced using the Web Animations API.
nsCSSPropertyID mTransitionProperty;
AnimationValue mTransitionToValue;
// This is the start value to be used for a check for whether a
// transition is being reversed. Normally the same as
// mEffect->mProperties[0].mSegments[0].mFromValue, except when this
// transition started as the reversal of another in-progress transition
// or when the effect has been mutated using the Web Animations API.
//
// Needed so we can handle two reverses in a row.
AnimationValue mStartForReversingTest;
// Likewise, the portion (in value space) of the "full" reversed
// transition that we're actually covering. For example, if a :hover
// effect has a transition that moves the element 10px to the right
// (by changing 'left' from 0px to 10px), and the mouse moves in to
// the element (starting the transition) but then moves out after the
// transition has advanced 4px, the second transition (from 10px/4px
// to 0px) will have mReversePortion of 0.4. (If the mouse then moves
// in again when the transition is back to 2px, the mReversePortion
// for the third transition (from 0px/2px to 10px) will be 0.8.
double mReversePortion = 1.0;
Maybe<ReplacedTransitionProperties> mReplacedTransition;
};
} // namespace dom
template <>
struct AnimationTypeTraits<dom::CSSTransition> {
static nsAtom* ElementPropertyAtom() {
return nsGkAtoms::transitionsProperty;
}
static nsAtom* BeforePropertyAtom() {
return nsGkAtoms::transitionsOfBeforeProperty;
}
static nsAtom* AfterPropertyAtom() {
return nsGkAtoms::transitionsOfAfterProperty;
}
static nsAtom* MarkerPropertyAtom() {
return nsGkAtoms::transitionsOfMarkerProperty;
}
};
} // namespace mozilla
#endif // mozilla_dom_CSSTransition_h

View File

@ -4,6 +4,7 @@
#include "DocumentTimeline.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/DocumentTimelineBinding.h"
#include "AnimationUtils.h"
#include "nsContentUtils.h"
@ -58,8 +59,7 @@ already_AddRefed<DocumentTimeline> DocumentTimeline::Constructor(
if (originTime == TimeDuration::Forever() ||
originTime == -TimeDuration::Forever()) {
aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>(
NS_LITERAL_STRING("Origin time"));
aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>("Origin time");
return nullptr;
}
RefPtr<DocumentTimeline> timeline = new DocumentTimeline(doc, originTime);

View File

@ -269,6 +269,12 @@ void EffectCompositor::PostRestyleForAnimation(dom::Element* aElement,
return;
}
// FIXME: Bug 1615083 KeyframeEffect::SetTarget() and
// KeyframeEffect::SetPseudoElement() may set a non-existing pseudo element,
// and we still have to update its style, based on the wpt. However, we don't
// have the generated element here, so we failed the wpt.
//
// See wpt for more info: web-animations/interfaces/KeyframeEffect/target.html
dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
if (!element) {
return;
@ -389,19 +395,31 @@ static void ComposeSortedEffects(
const nsTArray<KeyframeEffect*>& aSortedEffects,
const EffectSet* aEffectSet, EffectCompositor::CascadeLevel aCascadeLevel,
RawServoAnimationValueMap* aAnimationValues) {
// If multiple animations affect the same property, animations with higher
// composite order (priority) override or add to animations with lower
// priority.
const bool isTransition =
aCascadeLevel == EffectCompositor::CascadeLevel::Transitions;
nsCSSPropertyIDSet propertiesToSkip;
// Transitions should be overridden by running animations of the same
// property per https://drafts.csswg.org/css-transitions/#application:
//
// > Implementations must add this value to the cascade if and only if that
// > property is not currently undergoing a CSS Animation on the same element.
//
// FIXME(emilio, bug 1606176): This should assert that
// aEffectSet->PropertiesForAnimationsLevel() is up-to-date, and it may not
// follow the spec in those cases. There are various places where we get style
// without flushing that would trigger the below assertion.
//
// MOZ_ASSERT_IF(aEffectSet, !aEffectSet->CascadeNeedsUpdate());
if (aEffectSet) {
propertiesToSkip =
aCascadeLevel == EffectCompositor::CascadeLevel::Animations
? aEffectSet->PropertiesForAnimationsLevel().Inverse()
: aEffectSet->PropertiesForAnimationsLevel();
isTransition ? aEffectSet->PropertiesForAnimationsLevel()
: aEffectSet->PropertiesForAnimationsLevel().Inverse();
}
for (KeyframeEffect* effect : aSortedEffects) {
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
auto* animation = effect->GetAnimation();
MOZ_ASSERT(!isTransition || animation->CascadeLevel() == aCascadeLevel);
animation->ComposeStyle(*aAnimationValues, propertiesToSkip);
}
}
@ -421,11 +439,27 @@ bool EffectCompositor::GetServoAnimationRule(
return false;
}
const bool isTransition = aCascadeLevel == CascadeLevel::Transitions;
// Get a list of effects sorted by composite order.
nsTArray<KeyframeEffect*> sortedEffectList(effectSet->Count());
for (KeyframeEffect* effect : *effectSet) {
if (isTransition &&
effect->GetAnimation()->CascadeLevel() != aCascadeLevel) {
// We may need to use transition rules for the animations level for the
// case of missing keyframes in animations, but we don't ever need to look
// at non-transition levels to build a transition rule. When the effect
// set information is out of date (see above), this avoids creating bogus
// transition rules, see bug 1605610.
continue;
}
sortedEffectList.AppendElement(effect);
}
if (sortedEffectList.IsEmpty()) {
return false;
}
sortedEffectList.Sort(EffectCompositeOrderComparator());
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
@ -444,14 +478,14 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(),
"Should not be in print preview");
Maybe<NonOwningAnimationTarget> target = aEffect.GetTarget();
NonOwningAnimationTarget target = aEffect.GetAnimationTarget();
if (!target) {
return false;
}
// Don't try to compose animations for elements in documents without a pres
// shell (e.g. XMLHttpRequest documents).
if (!nsContentUtils::GetPresShellForContent(target->mElement)) {
if (!nsContentUtils::GetPresShellForContent(target.mElement)) {
return false;
}
@ -459,11 +493,10 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
// where the cascade results are updated in the pre-traversal as needed.
// This function, however, is only called when committing styles so we
// need to ensure the cascade results are up-to-date manually.
EffectCompositor::MaybeUpdateCascadeResults(target->mElement,
target->mPseudoType);
MaybeUpdateCascadeResults(target.mElement, target.mPseudoType);
EffectSet* effectSet =
EffectSet::GetEffectSet(target->mElement, target->mPseudoType);
EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
// Get a list of effects sorted by composite order up to and including
// |aEffect|, even if it is not in the EffectSet.
@ -483,9 +516,9 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
aAnimationValues);
MOZ_ASSERT(effectSet ==
EffectSet::GetEffectSet(target->mElement, target->mPseudoType),
"EffectSet should not change while composing style");
MOZ_ASSERT(
effectSet == EffectSet::GetEffectSet(target.mElement, target.mPseudoType),
"EffectSet should not change while composing style");
return true;
}

View File

@ -5,6 +5,7 @@
#include "EffectSet.h"
#include "mozilla/dom/Element.h" // For Element
#include "mozilla/RestyleManager.h"
#include "mozilla/LayerAnimationInfo.h"
#include "nsCSSPseudoElements.h" // For PseudoStyleType
#include "nsCycleCollectionNoteChild.h" // For CycleCollectionNoteChild
#include "nsPresContext.h"
@ -135,7 +136,7 @@ void EffectSet::DestroyEffectSet(dom::Element* aElement,
"Should not destroy an effect set while it is being enumerated");
effectSet = nullptr;
aElement->DeleteProperty(propName);
aElement->RemoveProperty(propName);
}
void EffectSet::UpdateAnimationGeneration(nsPresContext* aPresContext) {

View File

@ -61,17 +61,10 @@ struct PropertyValuePair {
struct Keyframe {
Keyframe() = default;
Keyframe(const Keyframe& aOther) = default;
Keyframe(Keyframe&& aOther) { *this = std::move(aOther); }
Keyframe(Keyframe&& aOther) = default;
Keyframe& operator=(const Keyframe& aOther) = default;
Keyframe& operator=(Keyframe&& aOther) {
mOffset = aOther.mOffset;
mComputedOffset = aOther.mComputedOffset;
mTimingFunction = std::move(aOther.mTimingFunction);
mComposite = std::move(aOther.mComposite);
mPropertyValues = std::move(aOther.mPropertyValues);
return *this;
}
Keyframe& operator=(Keyframe&& aOther) = default;
Maybe<double> mOffset;
static constexpr double kComputedOffsetNotSet = -1.0;

View File

@ -8,7 +8,6 @@
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
// For UnrestrictedDoubleOrKeyframeAnimationOptions;
#include "mozilla/dom/CSSPseudoElement.h"
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/MutationObservers.h"
#include "mozilla/AnimationUtils.h"
@ -34,13 +33,14 @@
#include "nsCSSPseudoElements.h" // For PseudoStyleType
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
#include "nsIFrame.h"
#include "nsIFrameInlines.h"
#include "nsPresContextInlines.h"
#include "nsRefreshDriver.h"
namespace mozilla {
void AnimationProperty::SetPerformanceWarning(
const AnimationPerformanceWarning& aWarning, const Element* aElement) {
const AnimationPerformanceWarning& aWarning, const dom::Element* aElement) {
if (mPerformanceWarning && *mPerformanceWarning == aWarning) {
return;
}
@ -71,7 +71,8 @@ bool PropertyValuePair::operator==(const PropertyValuePair& aOther) const {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffect, AnimationEffect, mTarget)
NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffect, AnimationEffect,
mTarget.mElement)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffect, AnimationEffect)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
@ -83,11 +84,11 @@ NS_IMPL_ADDREF_INHERITED(KeyframeEffect, AnimationEffect)
NS_IMPL_RELEASE_INHERITED(KeyframeEffect, AnimationEffect)
KeyframeEffect::KeyframeEffect(Document* aDocument,
const Maybe<OwningAnimationTarget>& aTarget,
OwningAnimationTarget&& aTarget,
TimingParams&& aTiming,
const KeyframeEffectParams& aOptions)
: AnimationEffect(aDocument, std::move(aTiming)),
mTarget(aTarget),
mTarget(std::move(aTarget)),
mEffectOptions(aOptions),
mCumulativeChangeHint(nsChangeHint(0)) {}
@ -140,7 +141,7 @@ void KeyframeEffect::SetComposite(const CompositeOperation& aComposite) {
void KeyframeEffect::NotifySpecifiedTimingUpdated() {
// Use the same document for a pseudo element and its parent element.
// Use nullptr if we don't have mTarget, so disable the mutation batch.
nsAutoAnimationMutationBatch mb(mTarget ? mTarget->mElement->OwnerDoc()
nsAutoAnimationMutationBatch mb(mTarget ? mTarget.mElement->OwnerDoc()
: nullptr);
if (mAnimation) {
@ -221,7 +222,7 @@ void KeyframeEffect::SetKeyframes(JSContext* aContext,
JS::Handle<JSObject*> aKeyframes,
ErrorResult& aRv) {
nsTArray<Keyframe> keyframes = KeyframeUtils::GetKeyframesFromObject(
aContext, mDocument, aKeyframes, "KeyframeEffect.setKeyframes: ", aRv);
aContext, mDocument, aKeyframes, "KeyframeEffect.setKeyframes", aRv);
if (aRv.Failed()) {
return;
}
@ -250,6 +251,36 @@ void KeyframeEffect::SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
}
}
void KeyframeEffect::ReplaceTransitionStartValue(AnimationValue&& aStartValue) {
if (!aStartValue.mServo) {
return;
}
// A typical transition should have a single property and a single segment.
//
// (And for atypical transitions, that is, those updated by script, we don't
// apply the replacing behavior.)
if (mProperties.Length() != 1 || mProperties[0].mSegments.Length() != 1) {
return;
}
// Likewise, check that the keyframes are of the expected shape.
if (mKeyframes.Length() != 2 || mKeyframes[0].mPropertyValues.Length() != 1) {
return;
}
// Check that the value we are about to substitute in is actually for the
// same property.
if (Servo_AnimationValue_GetPropertyId(aStartValue.mServo) !=
mProperties[0].mProperty) {
return;
}
mKeyframes[0].mPropertyValues[0].mServoDeclarationBlock =
Servo_AnimationValue_Uncompute(aStartValue.mServo).Consume();
mProperties[0].mSegments[0].mFromValue = std::move(aStartValue);
}
static bool IsEffectiveProperty(const EffectSet& aEffects,
nsCSSPropertyID aProperty) {
return !aEffects.PropertiesWithImportantRules().HasProperty(aProperty) ||
@ -258,8 +289,9 @@ static bool IsEffectiveProperty(const EffectSet& aEffects,
const AnimationProperty* KeyframeEffect::GetEffectiveAnimationOfProperty(
nsCSSPropertyID aProperty, const EffectSet& aEffects) const {
MOZ_ASSERT(&aEffects ==
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType));
MOZ_ASSERT(mTarget &&
&aEffects == EffectSet::GetEffectSet(mTarget.mElement,
mTarget.mPseudoType));
for (const AnimationProperty& property : mProperties) {
if (aProperty != property.mProperty) {
@ -291,7 +323,7 @@ bool KeyframeEffect::HasEffectiveAnimationOfPropertySet(
nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
EffectSet& aEffects, const nsIFrame* aFrame) const {
MOZ_ASSERT(&aEffects ==
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType));
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType));
nsCSSPropertyIDSet properties;
@ -372,6 +404,16 @@ bool SpecifiedKeyframeArraysAreEqual(const nsTArray<Keyframe>& aA,
}
#endif
static bool HasCurrentColor(
const nsTArray<AnimationPropertySegment>& aSegments) {
for (const AnimationPropertySegment& segment : aSegments) {
if ((!segment.mFromValue.IsNull() && segment.mFromValue.IsCurrentColor()) ||
(!segment.mToValue.IsNull() && segment.mToValue.IsCurrentColor())) {
return true;
}
}
return false;
}
void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
MOZ_ASSERT(aStyle);
@ -391,7 +433,7 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
}
// Check if we need to update the cumulative change hint because we now have
// style data.
if (mNeedsStyleData && mTarget && mTarget->mElement->HasServoData()) {
if (mNeedsStyleData && mTarget && mTarget.mElement->HasServoData()) {
CalculateCumulativeChangeHint(aStyle);
}
return;
@ -409,9 +451,19 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
mProperties = std::move(properties);
UpdateEffectSet();
mHasCurrentColor = false;
for (AnimationProperty& property : mProperties) {
property.mIsRunningOnCompositor =
runningOnCompositorProperties.HasProperty(property.mProperty);
if (property.mProperty == eCSSProperty_background_color &&
!mHasCurrentColor) {
if (HasCurrentColor(property.mSegments)) {
mHasCurrentColor = true;
break;
}
}
}
CalculateCumulativeChangeHint(aStyle);
@ -444,7 +496,7 @@ void KeyframeEffect::EnsureBaseStyles(
mBaseValues.Clear();
nsPresContext* presContext =
nsContentUtils::GetContextForContent(mTarget->mElement);
nsContentUtils::GetContextForContent(mTarget.mElement);
// If |aProperties| is empty we're not going to dereference |presContext| so
// we don't care if it is nullptr.
//
@ -493,8 +545,10 @@ void KeyframeEffect::EnsureBaseStyle(
}
if (!aBaseComputedStyle) {
MOZ_ASSERT(mTarget, "Should have a valid target");
Element* animatingElement = EffectCompositor::GetElementToRestyle(
mTarget->mElement, mTarget->mPseudoType);
mTarget.mElement, mTarget.mPseudoType);
if (!animatingElement) {
return;
}
@ -505,7 +559,7 @@ void KeyframeEffect::EnsureBaseStyle(
Servo_ComputedValues_ExtractAnimationValue(aBaseComputedStyle,
aProperty.mProperty)
.Consume();
mBaseValues.Put(aProperty.mProperty, baseValue);
mBaseValues.Put(aProperty.mProperty, std::move(baseValue));
}
void KeyframeEffect::WillComposeStyle() {
@ -578,9 +632,9 @@ void KeyframeEffect::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
// out of view.
if (HasPropertiesThatMightAffectOverflow()) {
nsPresContext* presContext =
nsContentUtils::GetContextForContent(mTarget->mElement);
nsContentUtils::GetContextForContent(mTarget.mElement);
EffectSet* effectSet =
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
if (presContext && effectSet) {
TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
effectSet->UpdateLastOverflowAnimationSyncTime(now);
@ -646,6 +700,12 @@ void KeyframeEffect::ResetIsRunningOnCompositor() {
}
}
static bool IsSupportedPseudoForWebAnimation(PseudoStyleType aType) {
// FIXME: Bug 1615469: Support first-line and first-letter for Web Animation.
return aType == PseudoStyleType::before || aType == PseudoStyleType::after ||
aType == PseudoStyleType::marker;
}
static const KeyframeEffectOptions& KeyframeEffectOptionsFromUnion(
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions) {
MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
@ -660,50 +720,52 @@ static const KeyframeEffectOptions& KeyframeEffectOptionsFromUnion(
template <class OptionsType>
static KeyframeEffectParams KeyframeEffectParamsFromUnion(
const OptionsType& aOptions, CallerType aCallerType) {
const OptionsType& aOptions, CallerType aCallerType, ErrorResult& aRv) {
KeyframeEffectParams result;
if (aOptions.IsUnrestrictedDouble() ||
// Ignore iterationComposite and composite if the corresponding pref is
// not set. The default value 'Replace' will be used instead.
!StaticPrefs::dom_animations_api_compositing_enabled()) {
if (aOptions.IsUnrestrictedDouble()) {
return result;
}
const KeyframeEffectOptions& options =
KeyframeEffectOptionsFromUnion(aOptions);
// If dom.animations-api.compositing.enabled is turned off,
// iterationComposite and composite are the default value 'replace' in the
// dictionary.
result.mIterationComposite = options.mIterationComposite;
result.mComposite = options.mComposite;
return result;
}
/* static */
Maybe<OwningAnimationTarget> KeyframeEffect::ConvertTarget(
const Nullable<ElementOrCSSPseudoElement>& aTarget) {
// Return value optimization.
Maybe<OwningAnimationTarget> result;
if (aTarget.IsNull()) {
result.mPseudoType = PseudoStyleType::NotPseudo;
if (DOMStringIsNull(options.mPseudoElement)) {
return result;
}
const ElementOrCSSPseudoElement& target = aTarget.Value();
MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
"Uninitialized target");
if (target.IsElement()) {
result.emplace(&target.GetAsElement());
} else {
RefPtr<Element> elem = target.GetAsCSSPseudoElement().Element();
result.emplace(elem, target.GetAsCSSPseudoElement().GetType());
RefPtr<nsAtom> pseudoAtom =
nsCSSPseudoElements::GetPseudoAtom(options.mPseudoElement);
if (!pseudoAtom) {
// Per the spec, we throw SyntaxError for syntactically invalid pseudos.
aRv.ThrowSyntaxError(
nsPrintfCString("'%s' is a syntactically invalid pseudo-element.",
NS_ConvertUTF16toUTF8(options.mPseudoElement).get()));
return result;
}
result.mPseudoType = nsCSSPseudoElements::GetPseudoType(
pseudoAtom, CSSEnabledState::ForAllContent);
if (!IsSupportedPseudoForWebAnimation(result.mPseudoType)) {
// Per the spec, we throw SyntaxError for unsupported pseudos.
aRv.ThrowSyntaxError(
nsPrintfCString("'%s' is an unsupported pseudo-element.",
NS_ConvertUTF16toUTF8(options.mPseudoElement).get()));
}
return result;
}
template <class OptionsType>
/* static */
already_AddRefed<KeyframeEffect> KeyframeEffect::ConstructKeyframeEffect(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
const GlobalObject& aGlobal, Element* aTarget,
JS::Handle<JSObject*> aKeyframes, const OptionsType& aOptions,
ErrorResult& aRv) {
// We should get the document from `aGlobal` instead of the current Realm
@ -721,18 +783,22 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::ConstructKeyframeEffect(
return nullptr;
}
KeyframeEffectParams effectOptions =
KeyframeEffectParamsFromUnion(aOptions, aGlobal.CallerType(), aRv);
// An invalid Pseudo-element aborts all further steps.
if (aRv.Failed()) {
return nullptr;
}
TimingParams timingParams =
TimingParams::FromOptionsUnion(aOptions, doc, aRv);
if (aRv.Failed()) {
return nullptr;
}
KeyframeEffectParams effectOptions =
KeyframeEffectParamsFromUnion(aOptions, aGlobal.CallerType());
Maybe<OwningAnimationTarget> target = ConvertTarget(aTarget);
RefPtr<KeyframeEffect> effect =
new KeyframeEffect(doc, target, std::move(timingParams), effectOptions);
RefPtr<KeyframeEffect> effect = new KeyframeEffect(
doc, OwningAnimationTarget(aTarget, effectOptions.mPseudoType),
std::move(timingParams), effectOptions);
effect->SetKeyframes(aGlobal.Context(), aKeyframes, aRv);
if (aRv.Failed()) {
@ -747,7 +813,8 @@ nsTArray<AnimationProperty> KeyframeEffect::BuildProperties(
MOZ_ASSERT(aStyle);
nsTArray<AnimationProperty> result;
// If mTarget is null, return an empty property array.
// If mTarget is false (i.e. mTarget.mElement is null), return an empty
// property array.
if (!mTarget) {
return result;
}
@ -761,7 +828,7 @@ nsTArray<AnimationProperty> KeyframeEffect::BuildProperties(
auto keyframesCopy(mKeyframes);
result = KeyframeUtils::GetAnimationPropertiesFromKeyframes(
keyframesCopy, mTarget->mElement, aStyle, mEffectOptions.mComposite);
keyframesCopy, mTarget.mElement, aStyle, mEffectOptions.mComposite);
#ifdef DEBUG
MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
@ -782,6 +849,50 @@ static void EnumerateContinuationsOrIBSplitSiblings(nsIFrame* aFrame,
}
}
void KeyframeEffect::UpdateTarget(Element* aElement,
PseudoStyleType aPseudoType) {
OwningAnimationTarget newTarget(aElement, aPseudoType);
if (mTarget == newTarget) {
// Assign the same target, skip it.
return;
}
if (mTarget) {
UnregisterTarget();
ResetIsRunningOnCompositor();
RequestRestyle(EffectCompositor::RestyleType::Layer);
nsAutoAnimationMutationBatch mb(mTarget.mElement->OwnerDoc());
if (mAnimation) {
MutationObservers::NotifyAnimationRemoved(mAnimation);
}
}
mTarget = newTarget;
if (mTarget) {
UpdateTargetRegistration();
RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle(Flush::None);
if (computedStyle) {
UpdateProperties(computedStyle);
}
RequestRestyle(EffectCompositor::RestyleType::Layer);
nsAutoAnimationMutationBatch mb(mTarget.mElement->OwnerDoc());
if (mAnimation) {
MutationObservers::NotifyAnimationAdded(mAnimation);
mAnimation->ReschedulePendingTasks();
}
}
if (mAnimation) {
mAnimation->NotifyEffectTargetUpdated();
}
}
void KeyframeEffect::UpdateTargetRegistration() {
if (!mTarget) {
return;
@ -799,8 +910,8 @@ void KeyframeEffect::UpdateTargetRegistration() {
"Out of date Animation::IsRelevant value");
if (isRelevant && !mInEffectSet) {
EffectSet* effectSet = EffectSet::GetOrCreateEffectSet(
mTarget->mElement, mTarget->mPseudoType);
EffectSet* effectSet =
EffectSet::GetOrCreateEffectSet(mTarget.mElement, mTarget.mPseudoType);
effectSet->AddEffect(*this);
mInEffectSet = true;
UpdateEffectSet(effectSet);
@ -818,7 +929,7 @@ void KeyframeEffect::UnregisterTarget() {
}
EffectSet* effectSet =
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
MOZ_ASSERT(effectSet,
"If mInEffectSet is true, there must be an EffectSet"
" on the target element");
@ -827,7 +938,7 @@ void KeyframeEffect::UnregisterTarget() {
effectSet->RemoveEffect(*this);
if (effectSet->IsEmpty()) {
EffectSet::DestroyEffectSet(mTarget->mElement, mTarget->mPseudoType);
EffectSet::DestroyEffectSet(mTarget.mElement, mTarget.mPseudoType);
}
}
nsIFrame* frame = GetPrimaryFrame();
@ -841,10 +952,10 @@ void KeyframeEffect::RequestRestyle(
return;
}
nsPresContext* presContext =
nsContentUtils::GetContextForContent(mTarget->mElement);
nsContentUtils::GetContextForContent(mTarget.mElement);
if (presContext && mAnimation) {
presContext->EffectCompositor()->RequestRestyle(
mTarget->mElement, mTarget->mPseudoType, aRestyleType,
mTarget.mElement, mTarget.mPseudoType, aRestyleType,
mAnimation->CascadeLevel());
}
}
@ -858,17 +969,15 @@ already_AddRefed<ComputedStyle> KeyframeEffect::GetTargetComputedStyle(
MOZ_ASSERT(mTarget,
"Should only have a document when we have a target element");
nsAtom* pseudo =
PseudoStyle::IsPseudoElement(mTarget->mPseudoType)
? nsCSSPseudoElements::GetPseudoAtom(mTarget->mPseudoType)
: nullptr;
nsAtom* pseudo = PseudoStyle::IsPseudoElement(mTarget.mPseudoType)
? nsCSSPseudoElements::GetPseudoAtom(mTarget.mPseudoType)
: nullptr;
OwningAnimationTarget kungfuDeathGrip(mTarget->mElement,
mTarget->mPseudoType);
OwningAnimationTarget kungfuDeathGrip(mTarget.mElement, mTarget.mPseudoType);
return aFlushType == Flush::Style
? nsComputedDOMStyle::GetComputedStyle(mTarget->mElement, pseudo)
: nsComputedDOMStyle::GetComputedStyleNoFlush(mTarget->mElement,
? nsComputedDOMStyle::GetComputedStyle(mTarget.mElement, pseudo)
: nsComputedDOMStyle::GetComputedStyleNoFlush(mTarget.mElement,
pseudo);
}
@ -891,8 +1000,7 @@ void DumpAnimationProperties(
/* static */
already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
const GlobalObject& aGlobal, Element* aTarget,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv) {
@ -901,8 +1009,7 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
/* static */
already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
const GlobalObject& aGlobal, Element* aTarget,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aRv) {
@ -925,8 +1032,8 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
// Note: we don't need to re-throw exceptions since the value specified on
// aSource's timing object can be assumed valid.
RefPtr<KeyframeEffect> effect = new KeyframeEffect(
doc, aSource.mTarget, TimingParams(aSource.SpecifiedTiming()),
aSource.mEffectOptions);
doc, OwningAnimationTarget(aSource.mTarget),
TimingParams(aSource.SpecifiedTiming()), aSource.mEffectOptions);
// Copy cumulative change hint. mCumulativeChangeHint should be the same as
// the source one because both of targets are the same.
effect->mCumulativeChangeHint = aSource.mCumulativeChangeHint;
@ -936,76 +1043,45 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
// computed offsets and rebuild the animation properties.
effect->mKeyframes = aSource.mKeyframes;
effect->mProperties = aSource.mProperties;
for (auto iter = aSource.mBaseValues.ConstIter(); !iter.Done(); iter.Next()) {
// XXX Should this use non-const Iter() and then pass
// std::move(iter.Data())? Otherwise aSource might be a const&...
effect->mBaseValues.Put(iter.Key(), RefPtr{iter.Data()});
}
return effect.forget();
}
void KeyframeEffect::GetTarget(
Nullable<OwningElementOrCSSPseudoElement>& aRv) const {
if (!mTarget) {
aRv.SetNull();
void KeyframeEffect::SetPseudoElement(const nsAString& aPseudoElement,
ErrorResult& aRv) {
PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
if (DOMStringIsNull(aPseudoElement)) {
UpdateTarget(mTarget.mElement, pseudoType);
return;
}
switch (mTarget->mPseudoType) {
case PseudoStyleType::before:
case PseudoStyleType::after:
case PseudoStyleType::marker:
aRv.SetValue().SetAsCSSPseudoElement() =
CSSPseudoElement::GetCSSPseudoElement(mTarget->mElement,
mTarget->mPseudoType);
break;
case PseudoStyleType::NotPseudo:
aRv.SetValue().SetAsElement() = mTarget->mElement;
break;
default:
MOZ_ASSERT_UNREACHABLE("Animation of unsupported pseudo-type");
aRv.SetNull();
}
}
void KeyframeEffect::SetTarget(
const Nullable<ElementOrCSSPseudoElement>& aTarget) {
Maybe<OwningAnimationTarget> newTarget = ConvertTarget(aTarget);
if (mTarget == newTarget) {
// Assign the same target, skip it.
// Note: GetPseudoAtom() also returns nullptr for the null string,
// so we handle null case before this.
RefPtr<nsAtom> pseudoAtom =
nsCSSPseudoElements::GetPseudoAtom(aPseudoElement);
if (!pseudoAtom) {
// Per the spec, we throw SyntaxError for syntactically invalid pseudos.
aRv.ThrowSyntaxError(
nsPrintfCString("'%s' is a syntactically invalid pseudo-element.",
NS_ConvertUTF16toUTF8(aPseudoElement).get()));
return;
}
if (mTarget) {
UnregisterTarget();
ResetIsRunningOnCompositor();
RequestRestyle(EffectCompositor::RestyleType::Layer);
nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc());
if (mAnimation) {
MutationObservers::NotifyAnimationRemoved(mAnimation);
}
pseudoType = nsCSSPseudoElements::GetPseudoType(
pseudoAtom, CSSEnabledState::ForAllContent);
if (!IsSupportedPseudoForWebAnimation(pseudoType)) {
// Per the spec, we throw SyntaxError for unsupported pseudos.
aRv.ThrowSyntaxError(
nsPrintfCString("'%s' is an unsupported pseudo-element.",
NS_ConvertUTF16toUTF8(aPseudoElement).get()));
return;
}
mTarget = newTarget;
if (mTarget) {
UpdateTargetRegistration();
RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle(Flush::None);
if (computedStyle) {
UpdateProperties(computedStyle);
}
RequestRestyle(EffectCompositor::RestyleType::Layer);
nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc());
if (mAnimation) {
MutationObservers::NotifyAnimationAdded(mAnimation);
mAnimation->ReschedulePendingTasks();
}
}
if (mAnimation) {
mAnimation->NotifyEffectTargetUpdated();
}
UpdateTarget(mTarget.mElement, pseudoType);
}
static void CreatePropertyValue(
@ -1064,9 +1140,14 @@ void KeyframeEffect::GetProperties(
if (segment.mFromKey == segment.mToKey) {
fromValue.mEasing.Reset();
}
// The following won't fail since we have already allocated the capacity
// above.
propertyDetails.mValues.AppendElement(fromValue, mozilla::fallible);
// Even though we called SetCapacity before, this could fail, since we
// might add multiple elements to propertyDetails.mValues for an element
// of property.mSegments in the cases mentioned below.
if (!propertyDetails.mValues.AppendElement(fromValue,
mozilla::fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
// Normally we can ignore the to-value for this segment since it is
// identical to the from-value from the next segment. However, we need
@ -1082,7 +1163,11 @@ void KeyframeEffect::GetProperties(
// last property value or before a sudden jump so we just drop the
// easing property altogether.
toValue.mEasing.Reset();
propertyDetails.mValues.AppendElement(toValue, mozilla::fallible);
if (!propertyDetails.mValues.AppendElement(toValue,
mozilla::fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
}
}
@ -1090,7 +1175,7 @@ void KeyframeEffect::GetProperties(
}
}
void KeyframeEffect::GetKeyframes(JSContext*& aCx, nsTArray<JSObject*>& aResult,
void KeyframeEffect::GetKeyframes(JSContext* aCx, nsTArray<JSObject*>& aResult,
ErrorResult& aRv) const {
MOZ_ASSERT(aResult.IsEmpty());
MOZ_ASSERT(!aRv.Failed());
@ -1140,6 +1225,8 @@ void KeyframeEffect::GetKeyframes(JSContext*& aCx, nsTArray<JSObject*>& aResult,
keyframe.mTimingFunction.ref().AppendToString(keyframeDict.mEasing);
} // else if null, leave easing as its default "linear".
// With the pref off (i.e. dom.animations-api.compositing.enabled:false),
// the dictionary-to-JS conversion will skip this member entirely.
keyframeDict.mComposite = keyframe.mComposite;
JS::Rooted<JS::Value> keyframeJSValue(aCx);
@ -1227,6 +1314,46 @@ KeyframeEffect::OverflowRegionRefreshInterval() {
return kOverflowRegionRefreshInterval;
}
static bool IsDefinitivelyInvisibleDueToOpacity(const nsIFrame& aFrame) {
if (!aFrame.Style()->IsInOpacityZeroSubtree()) {
return false;
}
// Find the root of the opacity: 0 subtree.
const nsIFrame* root = &aFrame;
while (true) {
auto* parent = root->GetInFlowParent();
if (!parent || !parent->Style()->IsInOpacityZeroSubtree()) {
break;
}
root = parent;
}
MOZ_ASSERT(root && root->Style()->IsInOpacityZeroSubtree());
// If aFrame is the root of the opacity: zero subtree, we can't prove we can
// optimize it away, because it may have an opacity animation itself.
if (root == &aFrame) {
return false;
}
// Even if we're in an opacity: zero subtree, if the root of the subtree may
// have an opacity animation, we can't optimize us away, as we may become
// visible ourselves.
return !root->HasAnimationOfOpacity();
}
static bool CanOptimizeAwayDueToOpacity(const KeyframeEffect& aEffect,
const nsIFrame& aFrame) {
if (!aFrame.Style()->IsInOpacityZeroSubtree()) {
return false;
}
if (IsDefinitivelyInvisibleDueToOpacity(aFrame)) {
return true;
}
return !aEffect.HasOpacityChange() && !aFrame.HasAnimationOfOpacity();
}
bool KeyframeEffect::CanThrottleIfNotVisible(nsIFrame& aFrame) const {
// Unless we are newly in-effect, we can throttle the animation if the
// animation is paint only and the target frame is out of view or the document
@ -1242,8 +1369,13 @@ bool KeyframeEffect::CanThrottleIfNotVisible(nsIFrame& aFrame) const {
const bool isVisibilityHidden =
!aFrame.IsVisibleOrMayHaveVisibleDescendants();
if ((!isVisibilityHidden || HasVisibilityChange()) &&
!aFrame.IsScrolledOutOfView()) {
const bool canOptimizeAwayVisibility =
isVisibilityHidden && !HasVisibilityChange();
const bool invisible = canOptimizeAwayVisibility ||
CanOptimizeAwayDueToOpacity(*this, aFrame) ||
aFrame.IsScrolledOutOfView();
if (!invisible) {
return false;
}
@ -1302,7 +1434,7 @@ bool KeyframeEffect::CanThrottle() const {
"The property should be able to run on the compositor");
if (!effectSet) {
effectSet =
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
MOZ_ASSERT(effectSet,
"CanThrottle should be called on an effect "
"associated with a target element");
@ -1338,7 +1470,7 @@ bool KeyframeEffect::CanThrottleOverflowChanges(const nsIFrame& aFrame) const {
TimeStamp now = aFrame.PresContext()->RefreshDriver()->MostRecentRefresh();
EffectSet* effectSet =
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
MOZ_ASSERT(effectSet,
"CanOverflowTransformChanges is expected to be called"
" on an effect in an effect set");
@ -1367,7 +1499,7 @@ bool KeyframeEffect::CanThrottleOverflowChangesInScrollable(
// If we don't show scrollbars and have no intersection observers, we don't
// care about overflow.
if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars) == 0 &&
if (LookAndFeel::GetInt(LookAndFeel::IntID::ShowHideScrollbars) == 0 &&
!hasIntersectionObservers) {
return true;
}
@ -1415,16 +1547,16 @@ nsIFrame* KeyframeEffect::GetPrimaryFrame() const {
return frame;
}
if (mTarget->mPseudoType == PseudoStyleType::before) {
frame = nsLayoutUtils::GetBeforeFrame(mTarget->mElement);
} else if (mTarget->mPseudoType == PseudoStyleType::after) {
frame = nsLayoutUtils::GetAfterFrame(mTarget->mElement);
} else if (mTarget->mPseudoType == PseudoStyleType::marker) {
frame = nsLayoutUtils::GetMarkerFrame(mTarget->mElement);
if (mTarget.mPseudoType == PseudoStyleType::before) {
frame = nsLayoutUtils::GetBeforeFrame(mTarget.mElement);
} else if (mTarget.mPseudoType == PseudoStyleType::after) {
frame = nsLayoutUtils::GetAfterFrame(mTarget.mElement);
} else if (mTarget.mPseudoType == PseudoStyleType::marker) {
frame = nsLayoutUtils::GetMarkerFrame(mTarget.mElement);
} else {
frame = mTarget->mElement->GetPrimaryFrame();
MOZ_ASSERT(mTarget->mPseudoType == PseudoStyleType::NotPseudo,
"unknown mTarget->mPseudoType");
frame = mTarget.mElement->GetPrimaryFrame();
MOZ_ASSERT(mTarget.mPseudoType == PseudoStyleType::NotPseudo,
"unknown mTarget.mPseudoType");
}
return frame;
@ -1434,7 +1566,7 @@ Document* KeyframeEffect::GetRenderedDocument() const {
if (!mTarget) {
return nullptr;
}
return mTarget->mElement->GetComposedDoc();
return mTarget.mElement->GetComposedDoc();
}
PresShell* KeyframeEffect::GetPresShell() const {
@ -1516,7 +1648,7 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet,
AnimationPerformanceWarning::Type& aPerformanceWarning /* out */) const {
EffectSet* effectSet =
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
// The various transform properties ('transform', 'scale' etc.) get combined
// on the compositor.
//
@ -1535,6 +1667,10 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
return true;
}
const bool enableMainthreadSynchronizationWithGeometricAnimations =
StaticPrefs::
dom_animations_mainthread_synchronization_with_geometric_animations();
for (const AnimationProperty& property : mProperties) {
// If there is a property for animations level that is overridden by
// !important rules, it should not block other animations from running
@ -1551,7 +1687,8 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
continue;
}
// Check for geometric properties
if (IsGeometricProperty(property.mProperty)) {
if (enableMainthreadSynchronizationWithGeometricAnimations &&
IsGeometricProperty(property.mProperty)) {
aPerformanceWarning =
AnimationPerformanceWarning::Type::TransformWithGeometricProperties;
return true;
@ -1587,7 +1724,7 @@ void KeyframeEffect::SetPerformanceWarning(
if (!curr.HasProperty(property.mProperty)) {
continue;
}
property.SetPerformanceWarning(aWarning, mTarget->mElement);
property.SetPerformanceWarning(aWarning, mTarget.mElement);
curr.RemoveProperty(property.mProperty);
if (curr.IsEmpty()) {
return;
@ -1604,7 +1741,7 @@ KeyframeEffect::CreateComputedStyleForAnimationValue(
"with a valid ComputedStyle");
Element* elementForResolve = EffectCompositor::GetElementToRestyle(
mTarget->mElement, mTarget->mPseudoType);
mTarget.mElement, mTarget.mPseudoType);
// The element may be null if, for example, we target a pseudo-element that no
// longer exists.
if (!elementForResolve) {
@ -1622,7 +1759,7 @@ void KeyframeEffect::CalculateCumulativeChangeHint(
mNeedsStyleData = false;
nsPresContext* presContext =
mTarget ? nsContentUtils::GetContextForContent(mTarget->mElement)
mTarget ? nsContentUtils::GetContextForContent(mTarget.mElement)
: nullptr;
if (!presContext) {
// Change hints make no sense if we're not rendered.
@ -1715,7 +1852,7 @@ void KeyframeEffect::SetAnimation(Animation* aAnimation) {
}
bool KeyframeEffect::CanIgnoreIfNotVisible() const {
if (!AnimationUtils::IsOffscreenThrottlingEnabled()) {
if (!StaticPrefs::dom_animations_offscreen_throttling()) {
return false;
}
@ -1731,7 +1868,7 @@ void KeyframeEffect::MarkCascadeNeedsUpdate() {
}
EffectSet* effectSet =
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
if (!effectSet) {
return;
}
@ -1785,14 +1922,11 @@ bool KeyframeEffect::ContainsAnimatedScale(const nsIFrame* aFrame) const {
}
AnimationValue baseStyle = BaseStyle(prop.mProperty);
if (baseStyle.IsNull()) {
// If we failed to get the base style, we consider it has scale value
// here just to be safe.
return true;
}
gfx::Size size = baseStyle.GetScaleValue(aFrame);
if (size != gfx::Size(1.0f, 1.0f)) {
return true;
if (!baseStyle.IsNull()) {
gfx::Size size = baseStyle.GetScaleValue(aFrame);
if (size != gfx::Size(1.0f, 1.0f)) {
return true;
}
}
// This is actually overestimate because there are some cases that combining
@ -1825,7 +1959,7 @@ void KeyframeEffect::UpdateEffectSet(EffectSet* aEffectSet) const {
EffectSet* effectSet =
aEffectSet
? aEffectSet
: EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
: EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
if (!effectSet) {
return;
}
@ -1878,6 +2012,7 @@ KeyframeEffect::MatchForCompositor KeyframeEffect::IsMatchForCompositor(
// If we know that the animation is not visible, we don't need to send the
// animation to the compositor.
if (!aFrame->IsVisibleOrMayHaveVisibleDescendants() ||
IsDefinitivelyInvisibleDueToOpacity(*aFrame) ||
aFrame->IsScrolledOutOfView()) {
return KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty;
}
@ -1899,6 +2034,13 @@ KeyframeEffect::MatchForCompositor KeyframeEffect::IsMatchForCompositor(
}
}
// We can't run this background color animation on the compositor if there
// is any `current-color` keyframe.
if (mHasCurrentColor) {
aPerformanceWarning = AnimationPerformanceWarning::Type::HasCurrentColor;
return KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty;
}
return mAnimation->IsPlaying() ? KeyframeEffect::MatchForCompositor::Yes
: KeyframeEffect::MatchForCompositor::IfNeeded;
}

View File

@ -37,7 +37,6 @@ class nsIFrame;
namespace mozilla {
class AnimValuesStyleRule;
enum class PseudoStyleType : uint8_t;
class ErrorResult;
struct AnimationRule;
struct TimingParams;
@ -46,9 +45,7 @@ class ComputedStyle;
class PresShell;
namespace dom {
class ElementOrCSSPseudoElement;
class GlobalObject;
class OwningElementOrCSSPseudoElement;
class UnrestrictedDoubleOrKeyframeAnimationOptions;
class UnrestrictedDoubleOrKeyframeEffectOptions;
enum class IterationCompositeOperation : uint8_t;
@ -102,8 +99,6 @@ struct AnimationProperty {
const dom::Element* aElement);
};
struct ElementPropertyTransition;
namespace dom {
class Animation;
@ -111,8 +106,7 @@ class Document;
class KeyframeEffect : public AnimationEffect {
public:
KeyframeEffect(Document* aDocument,
const Maybe<OwningAnimationTarget>& aTarget,
KeyframeEffect(Document* aDocument, OwningAnimationTarget&& aTarget,
TimingParams&& aTiming, const KeyframeEffectParams& aOptions);
NS_DECL_ISUPPORTS_INHERITED
@ -126,8 +120,7 @@ class KeyframeEffect : public AnimationEffect {
// KeyframeEffect interface
static already_AddRefed<KeyframeEffect> Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
const GlobalObject& aGlobal, Element* aTarget,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv);
@ -139,28 +132,37 @@ class KeyframeEffect : public AnimationEffect {
// for use with for Animatable.animate.
// Not exposed to content.
static already_AddRefed<KeyframeEffect> Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
const GlobalObject& aGlobal, Element* aTarget,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aRv);
void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
Maybe<NonOwningAnimationTarget> GetTarget() const {
Maybe<NonOwningAnimationTarget> result;
if (mTarget) {
result.emplace(*mTarget);
}
return result;
already_AddRefed<Element> GetTarget() const {
RefPtr<Element> ret = mTarget.mElement;
return ret.forget();
}
// This method calls GetTargetComputedStyle which is not safe to use when
NonOwningAnimationTarget GetAnimationTarget() const {
return NonOwningAnimationTarget(mTarget.mElement, mTarget.mPseudoType);
}
void GetPseudoElement(nsAString& aRetVal) const {
if (mTarget.mPseudoType == PseudoStyleType::NotPseudo) {
SetDOMStringToNull(aRetVal);
return;
}
aRetVal = nsCSSPseudoElements::PseudoTypeAsString(mTarget.mPseudoType);
}
// These two setters call GetTargetComputedStyle which is not safe to use when
// we are in the middle of updating style. If we need to use this when
// updating style, we should pass the ComputedStyle into this method and use
// that to update the properties rather than calling
// GetComputedStyle.
void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
void SetTarget(Element* aTarget) {
UpdateTarget(aTarget, mTarget.mPseudoType);
}
void SetPseudoElement(const nsAString& aPseudoElement, ErrorResult& aRv);
void GetKeyframes(JSContext*& aCx, nsTArray<JSObject*>& aResult,
void GetKeyframes(JSContext* aCx, nsTArray<JSObject*>& aResult,
ErrorResult& aRv) const;
void GetProperties(nsTArray<AnimationPropertyDetails>& aProperties,
ErrorResult& aRv) const;
@ -176,11 +178,15 @@ class KeyframeEffect : public AnimationEffect {
void NotifyAnimationTimingUpdated(PostRestyleMode aPostRestyle);
void RequestRestyle(EffectCompositor::RestyleType aRestyleType);
void SetAnimation(Animation* aAnimation) override;
void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
ErrorResult& aRv);
virtual void SetKeyframes(JSContext* aContext,
JS::Handle<JSObject*> aKeyframes, ErrorResult& aRv);
void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
const ComputedStyle* aStyle);
// Replace the start value of the transition. This is used for updating
// transitions running on the compositor.
void ReplaceTransitionStartValue(AnimationValue&& aStartValue);
// Returns the set of properties affected by this effect regardless of
// whether any of these properties is overridden by an !important rule.
nsCSSPropertyIDSet GetPropertySet() const;
@ -277,7 +283,7 @@ class KeyframeEffect : public AnimationEffect {
AnimationPerformanceWarning::Type& aPerformanceWarning /* out */) const;
bool HasGeometricProperties() const;
bool AffectsGeometry() const override {
return GetTarget() && HasGeometricProperties();
return mTarget && HasGeometricProperties();
}
Document* GetRenderedDocument() const;
@ -341,16 +347,16 @@ class KeyframeEffect : public AnimationEffect {
const Nullable<double>& aProgressOnLastCompose,
uint64_t aCurrentIterationOnLastCompose);
bool HasOpacityChange() const {
return mCumulativeChangeHint & nsChangeHint_UpdateOpacityLayer;
}
protected:
~KeyframeEffect() override = default;
static Maybe<OwningAnimationTarget> ConvertTarget(
const Nullable<ElementOrCSSPseudoElement>& aTarget);
template <class OptionsType>
static already_AddRefed<KeyframeEffect> ConstructKeyframeEffect(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
const GlobalObject& aGlobal, Element* aTarget,
JS::Handle<JSObject*> aKeyframes, const OptionsType& aOptions,
ErrorResult& aRv);
@ -359,6 +365,9 @@ class KeyframeEffect : public AnimationEffect {
// needed.
nsTArray<AnimationProperty> BuildProperties(const ComputedStyle* aStyle);
// Helper for SetTarget() and SetPseudoElement().
void UpdateTarget(Element* aElement, PseudoStyleType aPseudoType);
// This effect is registered with its target element so long as:
//
// (a) It has a target element, and
@ -397,7 +406,7 @@ class KeyframeEffect : public AnimationEffect {
const ComputedStyle* aComputedValues,
RefPtr<ComputedStyle>& aBaseComputedValues);
Maybe<OwningAnimationTarget> mTarget;
OwningAnimationTarget mTarget;
KeyframeEffectParams mEffectOptions;
@ -432,6 +441,9 @@ class KeyframeEffect : public AnimationEffect {
// if our properties haven't changed.
bool mNeedsStyleData = false;
// True if there is any current-color for background color in this keyframes.
bool mHasCurrentColor = false;
// The non-animated values for properties in this effect that contain at
// least one animation value that is composited with the underlying value
// (i.e. it uses the additive or accumulate composite mode).

View File

@ -6,6 +6,7 @@
#define mozilla_KeyframeEffectParams_h
#include "mozilla/dom/KeyframeEffectBinding.h" // IterationCompositeOperation
#include "mozilla/PseudoStyleType.h" // PseudoStyleType
namespace mozilla {
@ -13,6 +14,7 @@ struct KeyframeEffectParams {
dom::IterationCompositeOperation mIterationComposite =
dom::IterationCompositeOperation::Replace;
dom::CompositeOperation mComposite = dom::CompositeOperation::Replace;
PseudoStyleType mPseudoType = PseudoStyleType::NotPseudo;
};
} // namespace mozilla

View File

@ -4,33 +4,35 @@
#include "mozilla/KeyframeUtils.h"
#include <algorithm> // For std::stable_sort, std::min
#include "js/ForOfIterator.h" // For JS::ForOfIterator
#include "jsapi.h" // For most JSAPI
#include "mozilla/ComputedStyle.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Move.h"
#include "mozilla/RangedArray.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoCSSParser.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/TimingParams.h"
#include "mozilla/dom/BaseKeyframeTypesBinding.h" // For FastBaseKeyframe etc.
#include "mozilla/dom/BindingCallContext.h"
#include "mozilla/dom/Document.h" // For Document::AreWebAnimationsImplicitKeyframesEnabled
#include "mozilla/dom/Element.h"
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/KeyframeEffect.h" // For PropertyValuesPair etc.
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/Nullable.h"
#include "jsapi.h" // For most JSAPI
#include "js/ForOfIterator.h" // For JS::ForOfIterator
#include "nsClassHashtable.h"
#include "nsContentUtils.h" // For GetContextForContent
#include "nsCSSPropertyIDSet.h"
#include "nsCSSProps.h"
#include "nsCSSPseudoElements.h" // For PseudoStyleType
#include "nsClassHashtable.h"
#include "nsContentUtils.h" // For GetContextForContent
#include "nsIScriptError.h"
#include "nsPresContextInlines.h"
#include "nsTArray.h"
#include <algorithm> // For std::stable_sort, std::min
using mozilla::dom::Nullable;
@ -380,7 +382,10 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
const char* aContext,
nsTArray<Keyframe>& aResult) {
JS::Rooted<JS::Value> value(aCx);
ErrorResult parseEasingResult;
// Parsing errors should only be reported after we have finished iterating
// through all values. If we have any early returns while iterating, we should
// ignore parsing errors.
IgnoredErrorResult parseEasingResult;
for (;;) {
bool done;
@ -395,16 +400,20 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
// value).
if (!value.isObject() && !value.isNullOrUndefined()) {
dom::ThrowErrorMessage<dom::MSG_NOT_OBJECT>(
aCx,
nsPrintfCString("%sElement of sequence<Keyframe> argument", aContext)
.get());
aCx, aContext, "Element of sequence<Keyframe> argument");
return false;
}
// Convert the JS value into a BaseKeyframe dictionary value.
dom::binding_detail::FastBaseKeyframe keyframeDict;
if (!keyframeDict.Init(aCx, value,
dom::BindingCallContext callCx(aCx, aContext);
if (!keyframeDict.Init(callCx, value,
"Element of sequence<Keyframe> argument")) {
// This may happen if the value type of the member of BaseKeyframe is
// invalid. e.g. `offset` only accept a double value, so if we provide a
// string, we enter this branch.
// Besides, keyframeDict.Init() should throw a Type Error message already,
// so we don't have to do it again.
return false;
}
@ -412,6 +421,7 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
if (!keyframe) {
return false;
}
if (!keyframeDict.mOffset.IsNull()) {
keyframe->mOffset.emplace(keyframeDict.mOffset.Value());
}
@ -969,8 +979,8 @@ static void GetKeyframeListFromPropertyIndexedKeyframe(
// Convert the object to a property-indexed keyframe dictionary to
// get its explicit dictionary members.
dom::binding_detail::FastBasePropertyIndexedKeyframe keyframeDict;
if (!keyframeDict.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument",
false)) {
// XXXbz Pass in the method name from callers and set up a BindingCallContext?
if (!keyframeDict.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument")) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}

View File

@ -75,7 +75,7 @@ class PendingAnimationTracker final {
void MarkAnimationsThatMightNeedSynchronization();
private:
~PendingAnimationTracker() {}
~PendingAnimationTracker() = default;
void EnsurePaintIsScheduled();

View File

@ -46,7 +46,8 @@ TimingParams TimingParams::FromOptionsType(const OptionsType& aOptions,
result.mDuration.emplace(
StickyTimeDuration::FromMilliseconds(durationInMs));
} else {
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
nsPrintfCString error("Duration value %g is less than 0", durationInMs);
aRv.ThrowTypeError(error);
return result;
}
result.Update();
@ -196,7 +197,8 @@ Maybe<ComputedTimingFunction> TimingParams::ParseEasing(
nsTimingFunction timingFunction;
RefPtr<URLExtraData> url = ServoCSSParser::GetURLExtraData(aDocument);
if (!ServoCSSParser::ParseEasing(aEasing, url, timingFunction)) {
aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(aEasing);
aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(
NS_ConvertUTF16toUTF8(aEasing));
return Nothing();
}

View File

@ -6,6 +6,7 @@
#define mozilla_TimingParams_h
#include "nsStringFwd.h"
#include "nsPrintfCString.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/UnionTypes.h" // For OwningUnrestrictedDoubleOrString
#include "mozilla/ComputedTimingFunction.h"
@ -88,27 +89,33 @@ struct TimingParams {
if (durationInMs >= 0) {
result.emplace(StickyTimeDuration::FromMilliseconds(durationInMs));
} else {
aRv.ThrowTypeError<dom::MSG_ENFORCE_RANGE_OUT_OF_RANGE>(
NS_LITERAL_STRING("duration"));
nsPrintfCString err("Duration (%g) must be nonnegative", durationInMs);
aRv.ThrowTypeError(err);
}
} else if (!aDuration.GetAsString().EqualsLiteral("auto")) {
aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>(
aDuration.GetAsString());
NS_ConvertUTF16toUTF8(aDuration.GetAsString()));
}
return result;
}
static void ValidateIterationStart(double aIterationStart, ErrorResult& aRv) {
if (aIterationStart < 0) {
aRv.ThrowTypeError<dom::MSG_ENFORCE_RANGE_OUT_OF_RANGE>(
NS_LITERAL_STRING("iterationStart"));
nsPrintfCString err("Iteration start (%g) must not be negative",
aIterationStart);
aRv.ThrowTypeError(err);
}
}
static void ValidateIterations(double aIterations, ErrorResult& aRv) {
if (IsNaN(aIterations) || aIterations < 0) {
aRv.ThrowTypeError<dom::MSG_ENFORCE_RANGE_OUT_OF_RANGE>(
NS_LITERAL_STRING("iterations"));
if (IsNaN(aIterations)) {
aRv.ThrowTypeError("Iterations must not be NaN");
return;
}
if (aIterations < 0) {
nsPrintfCString err("Iterations (%g) must not be negative", aIterations);
aRv.ThrowTypeError(err);
}
}
@ -152,6 +159,10 @@ struct TimingParams {
mDuration = std::move(aDuration);
Update();
}
void SetDuration(const Maybe<StickyTimeDuration>& aDuration) {
mDuration = aDuration;
Update();
}
const Maybe<StickyTimeDuration>& Duration() const { return mDuration; }
void SetDelay(const TimeDuration& aDelay) {

View File

@ -12,7 +12,9 @@ EXPORTS.mozilla.dom += [
'Animation.h',
'AnimationEffect.h',
'AnimationTimeline.h',
'CSSAnimation.h',
'CSSPseudoElement.h',
'CSSTransition.h',
'DocumentTimeline.h',
'KeyframeEffect.h',
]
@ -45,7 +47,9 @@ UNIFIED_SOURCES += [
'AnimationTimeline.cpp',
'AnimationUtils.cpp',
'ComputedTimingFunction.cpp',
'CSSAnimation.cpp',
'CSSPseudoElement.cpp',
'CSSTransition.cpp',
'DocumentTimeline.cpp',
'EffectCompositor.cpp',
'EffectSet.cpp',
@ -61,4 +65,6 @@ LOCAL_INCLUDES += [
'/layout/style',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -502,13 +502,14 @@ promise_test(t => {
var childBAnimations = childB.getAnimations();
var divBeforeAnimations =
docAnims.filter(x => (x.effect.target.element == div &&
x.effect.target.type == "::before"));
docAnims.filter(x => (x.effect.target == div &&
x.effect.pseudoElement == "::before"));
var divAfterAnimations =
docAnims.filter(x => (x.effect.target.element == div &&
x.effect.target.type == "::after"));
docAnims.filter(x => (x.effect.target == div &&
x.effect.pseudoElement == "::after"));
var childBPseudoAnimations =
docAnims.filter(x => x.effect.target.element == childB);
docAnims.filter(x => (x.effect.target == childB &&
x.effect.pseudoElement == "::before"));
var seekRecords;
// The order in which we get the corresponding records is currently

View File

@ -74,21 +74,6 @@ function assert_equals_records(actual, expected, desc) {
}
}
// Create a pseudo element
function createPseudo(test, element, type) {
addStyle(test, { '@keyframes anim': '',
['.pseudo::' + type]: 'animation: anim 10s; ' +
'content: \'\';' });
element.classList.add('pseudo');
var anims = document.getAnimations();
assert_true(anims.length >= 1);
var anim = anims[anims.length - 1];
assert_equals(anim.effect.target.element, element);
assert_equals(anim.effect.target.type, '::' + type);
anim.cancel();
return anim.effect.target;
}
function runTest() {
[ { subtree: false },
{ subtree: true }
@ -1518,10 +1503,11 @@ function runTest() {
test(t => {
var div = addDiv(t);
var pseudoTarget = createPseudo(t, div, 'before');
var observer = setupSynchronousObserver(t, div, true);
var anim = pseudoTarget.animate({ opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 200 * MS_PER_SEC,
pseudoElement: '::before' });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
@ -1561,13 +1547,13 @@ function runTest() {
test(t => {
var div = addDiv(t);
var pseudoTarget = createPseudo(t, div, 'before');
var observer = setupSynchronousObserver(t, div, false);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
var pAnim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC,
pseudoElement: "::before" });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],

View File

@ -1644,6 +1644,27 @@ function testImportantRuleOverride() {
'compositor because any of the properties has important rules');
}
function testCurrentColor() {
if (SpecialPowers.DOMWindowUtils.layerManagerType == 'WebRender') {
return; // skip this test until bug 1510030 landed.
}
promise_test(async t => {
const animation = addDivAndAnimate(t, { class: 'compositable' },
{ backgroundColor: [ 'currentColor',
'red' ] },
100 * MS_PER_SEC);
await waitForPaints();
assert_animation_property_state_equals(
animation.effect.getProperties(),
[ { property: 'background-color',
runningOnCompositor: false,
warning: 'CompositorAnimationWarningHasCurrentColor'
} ]);
}, 'Background color animations with `current-color` don\'t run on the '
+ 'compositor');
}
function start() {
var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1']
.getService(SpecialPowers.Ci.nsIStringBundleService);
@ -1663,6 +1684,7 @@ function start() {
testTooLargeFrame();
testTransformSVG();
testImportantRuleOverride();
testCurrentColor();
promise_test(async t => {
var div = addDiv(t, { class: 'compositable',

View File

@ -0,0 +1,22 @@
<html class="reftest-wait">
<script>
function start () {
const kf_effect =
new KeyframeEffect(document.documentElement,
{ opacity: ['', '1'] },
{ easing: 'step-end',
duration: 10000 } );
const copy = new KeyframeEffect(kf_effect);
const animation = new Animation(copy);
animation.reverse();
document.documentElement.getBoundingClientRect();
requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
}
document.addEventListener('DOMContentLoaded', start);
</script>
</html>

View File

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<script>
function start () {
const keyframe = new KeyframeEffect(undefined, {});
const animation = new Animation(keyframe, undefined);
// Make animation run backwards...
animation.playbackRate = -100;
// But then set the current time to the future so it becomes "current"...
animation.currentTime = 2055;
// After updating the playback rate to zero, however, it should no longer
// be "current" (and this takes effect immediately because |animation| is
// paused)...
animation.updatePlaybackRate(0);
// Now update the target and hope nothing goes wrong...
keyframe.target = div;
}
document.addEventListener('DOMContentLoaded', start)
</script>
</head>
<div id=div></div>
</html>

View File

@ -0,0 +1,23 @@
<html>
<head>
<style>
* {
transition-duration: 2s;
}
</style>
<script>
function start () {
const element = document.createElementNS('', 's');
const effect = new KeyframeEffect(document.documentElement, {}, 196);
document.documentElement.setAttribute('style', 'padding-left:3');
effect.updateTiming({ 'delay': 2723 });
const animations = document.getAnimations();
animations[0].effect = effect;
animations[0].updatePlaybackRate(-129);
effect.target = element;
}
document.addEventListener('DOMContentLoaded', start);
</script>
</head>
</html>

View File

@ -0,0 +1,15 @@
<html>
<head>
<script>
function start() {
const element = document.createElement('img')
element.animate([
{ 'easing': '' },
{ 'offset': 'o' },
], {})
}
window.addEventListener('load', start)
</script>
</head>
</html>

View File

@ -0,0 +1,15 @@
<html>
<head>
<script>
function start() {
const element = document.createElement('img')
element.animate([
{ 'easing': '' },
123,
], {})
}
window.addEventListener('load', start)
</script>
</head>
</html>

View File

@ -0,0 +1,10 @@
<html>
<head>
<script>
document.addEventListener('DOMContentLoaded', () => {
const keyframe = new KeyframeEffect(document.documentElement, [{}], {})
keyframe.setKeyframes([{ 'easing': '' }, { 'offset': 'o', }])
})
</script>
</head>
</html>

View File

@ -0,0 +1,15 @@
<!doctype html>
<html class="reftest-wait">
<head>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.documentElement.style.setProperty('transition-duration', '3s', '')
document.documentElement.style.setProperty('rotate', '2deg', undefined)
document.documentElement.style.setProperty('-moz-outline-radius-topleft', '2%', '')
const [anim_1, anim_0] = document.documentElement.getAnimations({})
anim_1.effect = anim_0.effect
document.documentElement.classList.remove("reftest-wait");
})
</script>
</head>
</html>

View File

@ -0,0 +1,20 @@
<!doctype html>
<html>
<head>
<script>
window.addEventListener('load', async () => {
const element = document.getElementById('target');
element.animate({
'all': ['initial']
}, {
'duration': 500,
'pseudoElement': '::marker',
})
element.hidden = false;
})
</script>
</head>
<ul>
<li id='target' hidden></li>
</ul>
</html>

View File

@ -44,3 +44,11 @@ pref(dom.animations-api.implicit-keyframes.enabled,true) load 1468294-1.html
pref(dom.animations-api.implicit-keyframes.enabled,true) load 1467277-1.html
load 1524480-1.html
load 1575926.html
pref(dom.animations-api.implicit-keyframes.enabled,true) load 1585770.html
load 1604500-1.html
pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.getAnimations.enabled,true) load 1611847.html
pref(dom.animations-api.core.enabled,true) load 1612891-1.html
pref(dom.animations-api.core.enabled,true) load 1612891-2.html
pref(dom.animations-api.core.enabled,true) load 1612891-3.html
pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.getAnimations.enabled,true) load 1633442.html
pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.implicit-keyframes.enabled,true) load 1633486.html

View File

@ -46,6 +46,7 @@ skip-if = (toolkit == 'android' && debug) || (os == 'win' && bits == 64) # Bug 1
[mozilla/test_distance_of_transform.html]
[mozilla/test_document_timeline_origin_time_range.html]
[mozilla/test_hide_and_show.html]
[mozilla/test_mainthread_synchronization_pref.html]
[mozilla/test_moz_prefixed_properties.html]
[mozilla/test_pending_animation_tracker.html]
[mozilla/test_restyles.html]

View File

@ -117,6 +117,21 @@ test(t => {
);
}, 'composite member is ignored on keyframes when using object notation');
test(t => {
const anim = addDiv(t).animate(
{ marginLeft: ['0px', '10px'] },
100 * MS_PER_SEC
);
for (let frame of anim.effect.getKeyframes()) {
assert_false(
'composite' in frame,
'The BaseComputedKeyframe.composite member is not present'
);
}
}, 'composite member is hidden from the result of ' +
'KeyframeEffect::getKeyframes()');
done();
</script>
</body>

View File

@ -15,11 +15,6 @@ test(t => {
}, 'Document.getAnimations() is not available when getAnimations pref is'
+ ' disabled');
test(t => {
assert_false('CSSPseudoElement' in window);
}, 'CSSPseudoElement interface is not available when getAnimations pref is'
+ ' disabled');
done();
</script>
</body>

View File

@ -1131,6 +1131,19 @@ waitForAllPaints(() => {
}
);
add_task_if_omta_enabled(
async function animation_visibility_and_opacity() {
const div = addDiv(null);
const animation1 = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
const animation2 = div.animate({ visibility: ['hidden', 'visible'] }, 100 * MS_PER_SEC);
await waitForAnimationReadyToRestyle(animation1);
await waitForAnimationReadyToRestyle(animation2);
const markers = await observeStyling(5);
is(markers.length, 5, 'The animation should not be throttled');
await ensureElementRemoval(div);
}
);
add_task(async function restyling_for_animation_on_orphaned_element() {
const div = addDiv(null);
const animation = div.animate({ marginLeft: [ '0px', '100px' ] },
@ -1956,6 +1969,90 @@ waitForAllPaints(() => {
await ensureElementRemoval(div);
});
add_task_if_omta_enabled(async function no_restyling_animations_in_opacity_zero_element() {
const div = addDiv(null, { style: 'animation: on-main-thread 100s infinite; opacity: 0' });
const animation = div.getAnimations()[0];
await waitForAnimationReadyToRestyle(animation);
const markers = await observeStyling(5);
is(markers.length, 0,
'Animations running on the main thread in opacity: 0 element ' +
'should never cause restyles');
await ensureElementRemoval(div);
});
add_task_if_omta_enabled(async function no_restyling_compositor_animations_in_opacity_zero_descendant() {
const container = addDiv(null, { style: 'opacity: 0' });
const child = addDiv(null, { style: 'animation: rotate 100s infinite;' });
container.appendChild(child);
const animation = child.getAnimations()[0];
await waitForAnimationReadyToRestyle(animation);
ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
const markers = await observeStyling(5);
is(markers.length, 0,
'Animations running on the compositor in opacity zero descendant element ' +
'should never cause restyles');
await ensureElementRemoval(container);
});
add_task_if_omta_enabled(async function no_restyling_compositor_animations_in_opacity_zero_descendant_abspos() {
const container = addDiv(null, { style: 'opacity: 0' });
const child = addDiv(null, { style: 'position: abspos; animation: rotate 100s infinite;' });
container.appendChild(child);
const animation = child.getAnimations()[0];
await waitForAnimationReadyToRestyle(animation);
ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
const markers = await observeStyling(5);
is(markers.length, 0,
'Animations running on the compositor in opacity zero abspos descendant element ' +
'should never cause restyles');
await ensureElementRemoval(container);
});
add_task_if_omta_enabled(async function restyling_main_thread_animations_in_opacity_zero_descendant_after_root_opacity_animation() {
const container = addDiv(null, { style: 'opacity: 0' });
const child = addDiv(null, { style: 'animation: on-main-thread 100s infinite;' });
container.appendChild(child);
// Animate the container from 1 to zero opacity and ensure the child animation is throttled then.
const containerAnimation = container.animate({ opacity: [ '1', '0' ] }, 100);
await containerAnimation.finished;
const animation = child.getAnimations()[0];
await waitForAnimationReadyToRestyle(animation);
const markers = await observeStyling(5);
is(markers.length, 0,
'Animations running on the compositor in opacity zero descendant element ' +
'should never cause restyles after root animation has finished');
await ensureElementRemoval(container);
});
add_task_if_omta_enabled(async function restyling_main_thread_animations_in_opacity_zero_descendant_during_root_opacity_animation() {
const container = addDiv(null, { style: 'opacity: 0; animation: opacity 100s infinite' });
const child = addDiv(null, { style: 'animation: on-main-thread 100s infinite;' });
container.appendChild(child);
const animation = child.getAnimations()[0];
await waitForAnimationReadyToRestyle(animation);
const markers = await observeStyling(5);
is(markers.length, 5,
'Animations in opacity zero descendant element ' +
'should not be throttled if root is animating opacity');
await ensureElementRemoval(container);
});
});
</script>

View File

@ -105,7 +105,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, 1e+39, 0, 0)';
flushComputedStyle(div);
div.style.marginLeft = '0px';
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
'cubic-bezier(0, ' + max_float + ', 0, 0)',
'y1 control point for CSS transition on upper boundary');
div.style.transition = '';
@ -114,7 +114,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, 1e+39)';
flushComputedStyle(div);
div.style.marginLeft = '0px';
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
'cubic-bezier(0, 0, 0, ' + max_float + ')',
'y2 control point for CSS transition on upper boundary');
div.style.transition = '';
@ -123,7 +123,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, -1e+39, 0, 0)';
flushComputedStyle(div);
div.style.marginLeft = '0px';
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
'cubic-bezier(0, ' + '-' + max_float + ', 0, 0)',
'y1 control point for CSS transition on lower boundary');
div.style.transition = '';
@ -132,7 +132,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, -1e+39)';
flushComputedStyle(div);
div.style.marginLeft = '0px';
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
'cubic-bezier(0, 0, 0, ' + '-' + max_float + ')',
'y2 control point for CSS transition on lower boundary');

View File

@ -0,0 +1,42 @@
<!doctype html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<style>
.compositable {
/* Element needs geometry to be eligible for layerization */
width: 100px;
height: 100px;
background-color: white;
}
</style>
<body>
<div id="log"></div>
<script>
'use strict';
promise_test(async t => {
await SpecialPowers.pushPrefEnv({
set: [[ 'dom.animations.mainthread-synchronization-with-geometric-animations', false ]]
});
const elemA = addDiv(t, { class: 'compositable' });
const elemB = addDiv(t, { class: 'compositable' });
const animA = elemA.animate({ transform: [ 'translate(0px)',
'translate(100px)' ] },
100 * MS_PER_SEC);
const animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
100 * MS_PER_SEC);
await waitForPaints();
assert_true(SpecialPowers.wrap(animA).isRunningOnCompositor,
'Transform animation should not synchronize with margin-left animation ' +
'created within the same tick with disabling the corresponding pref');
}, 'Transform animation should not synchronize with margin-left animation '
+ 'created within the same tick with disabling the corresponding pref');
</script>
</body>

View File

@ -719,11 +719,10 @@ void AudioChannelService::AudioChannelWindow::AppendAgent(
RequestAudioFocus(aAgent);
AppendAgentAndIncreaseAgentsNum(aAgent);
if (aAudible == AudibleState::eAudible) {
AudioAudibleChanged(aAgent, AudibleState::eAudible,
AudibleChangedReasons::eDataAudibleChanged);
} else if (IsEnableAudioCompetingForAllAgents() &&
aAudible != AudibleState::eAudible) {
AudioAudibleChanged(aAgent, aAudible,
AudibleChangedReasons::eDataAudibleChanged);
if (IsEnableAudioCompetingForAllAgents() &&
aAudible != AudibleState::eAudible) {
NotifyAudioCompetingChanged(aAgent);
}
}

View File

@ -49,6 +49,41 @@ class AudioChannelService final : public nsIObserver {
NS_DECL_NSIOBSERVER
/**
* We use `AudibleState` to represent the audible state of an owner of audio
* channel agent. Those information in AudioChannelWindow could help us to
* determine if a tab is being audible or not, in order to tell Chrome JS to
* show the sound indicator or delayed autoplay icon on the tab bar.
*
* - Sound indicator
* When a tab is playing sound, we would show the sound indicator on tab bar
* to tell users that this tab is producing sound now. In addition, the sound
* indicator also give users an ablility to mute or unmute tab.
*
* When an AudioChannelWindow first contains an agent with state `eAudible`,
* or an AudioChannelWindow losts its last agent with state `eAudible`, we
* would notify Chrome JS about those changes, to tell them that a tab has
* been being audible or not, in order to display or remove the indicator for
* a corresponding tab.
*
* - Delayed autoplay icon (Play Tab icon)
* When we enable delaying autoplay, which is to postpone the autoplay media
* for unvisited tab until it first goes to foreground, or user click the
* play tab icon to resume the delayed media.
*
* When an AudioChannelWindow first contains an agent with state `eAudible` or
* `eMaybeAudible`, we would notify Chrome JS about this change, in order to
* show the delayed autoplay tab icon to user, which is used to notice user
* there is a media being delayed starting, and then user can click the play
* tab icon to resume the start of media, or visit that tab to resume delayed
* media automatically.
*
* According to our UX design, we don't show this icon for inaudible media.
* The reason of showing the icon for a tab, where the agent starts with state
* `eMaybeAudible`, is because some video might be silent in the beginning
* but would soon become audible later.
*
* ---------------------------------------------------------------------------
*
* eNotAudible : agent is not audible
* eMaybeAudible : agent is not audible now, but it might be audible later
* eAudible : agent is audible now

View File

@ -14,6 +14,7 @@
#include "nsGkAtoms.h"
#include "nsINode.h"
#include "nsRange.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
@ -42,6 +43,10 @@ template nsresult AbstractRange::SetStartAndEndInternal(
template nsresult AbstractRange::SetStartAndEndInternal(
const RawRangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary, StaticRange* aRange);
template bool AbstractRange::MaybeCacheToReuse(nsRange& aInstance);
template bool AbstractRange::MaybeCacheToReuse(StaticRange& aInstance);
bool AbstractRange::sHasShutDown = false;
NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractRange)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange)
@ -68,12 +73,56 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AbstractRange)
// NOTE: If you need to change default value of members of AbstractRange,
// update nsRange::Create(nsINode* aNode) and ClearForReuse() too.
AbstractRange::AbstractRange(nsINode* aNode)
: mIsPositioned(false), mIsGenerated(false), mCalledByJS(false) {
Init(aNode);
}
void AbstractRange::Init(nsINode* aNode) {
MOZ_ASSERT(aNode, "range isn't in a document!");
mOwner = aNode->OwnerDoc();
}
// static
void AbstractRange::Shutdown() {
sHasShutDown = true;
if (nsTArray<RefPtr<nsRange>>* cachedRanges = nsRange::sCachedRanges) {
nsRange::sCachedRanges = nullptr;
cachedRanges->Clear();
delete cachedRanges;
}
if (nsTArray<RefPtr<StaticRange>>* cachedRanges =
StaticRange::sCachedRanges) {
StaticRange::sCachedRanges = nullptr;
cachedRanges->Clear();
delete cachedRanges;
}
}
// static
template <class RangeType>
bool AbstractRange::MaybeCacheToReuse(RangeType& aInstance) {
static const size_t kMaxRangeCache = 64;
// If the instance is not used by JS and the cache is not yet full, we
// should reuse it. Otherwise, delete it.
if (sHasShutDown || aInstance.GetWrapperMaybeDead() || aInstance.GetFlags() ||
(RangeType::sCachedRanges &&
RangeType::sCachedRanges->Length() == kMaxRangeCache)) {
return false;
}
aInstance.ClearForReuse();
if (!RangeType::sCachedRanges) {
RangeType::sCachedRanges = new nsTArray<RefPtr<RangeType>>(16);
}
RangeType::sCachedRanges->AppendElement(&aInstance);
return true;
}
nsINode* AbstractRange::GetClosestCommonInclusiveAncestor() const {
return mIsPositioned ? nsContentUtils::GetClosestCommonInclusiveAncestor(
mStart.Container(), mEnd.Container())

View File

@ -23,6 +23,11 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
AbstractRange() = delete;
explicit AbstractRange(const AbstractRange& aOther) = delete;
/**
* Called when the process is shutting down.
*/
static void Shutdown();
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbstractRange)
@ -83,6 +88,22 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange);
template <class RangeType>
static bool MaybeCacheToReuse(RangeType& aInstance);
void Init(nsINode* aNode);
private:
void ClearForReuse() {
mOwner = nullptr;
mStart = RangeBoundary();
mEnd = RangeBoundary();
mIsPositioned = false;
mIsGenerated = false;
mCalledByJS = false;
}
protected:
RefPtr<Document> mOwner;
RangeBoundary mStart;
RangeBoundary mEnd;
@ -94,6 +115,8 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
bool mIsGenerated;
// Used by nsRange, but this should have this for minimizing the size.
bool mCalledByJS;
static bool sHasShutDown;
};
} // namespace dom

View File

@ -0,0 +1,68 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Implementation of some generic iterators over ancestor nodes.
*
* Note that these keep raw pointers to the nodes they iterate from, and as
* such the DOM should not be mutated while they're in use. There are debug
* assertions (via nsMutationGuard) that check this in debug builds.
*/
#ifndef mozilla_dom_AncestorIterator_h
#define mozilla_dom_AncestorIterator_h
#include "nsINode.h"
#include "nsIContentInlines.h"
#include "FilteredNodeIterator.h"
namespace mozilla {
namespace dom {
#ifdef DEBUG
# define MUTATION_GUARD(class_name_) \
nsMutationGuard mMutationGuard; \
~class_name_() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
#else
# define MUTATION_GUARD(class_name_)
#endif
#define DEFINE_ANCESTOR_ITERATOR(name_, method_) \
class Inclusive##name_ { \
using Self = Inclusive##name_; \
\
public: \
explicit Inclusive##name_(const nsINode& aNode) \
: mCurrent(const_cast<nsINode*>(&aNode)) {} \
Self& begin() { return *this; } \
std::nullptr_t end() const { return nullptr; } \
bool operator!=(std::nullptr_t) const { return !!mCurrent; } \
void operator++() { mCurrent = mCurrent->method_(); } \
nsINode* operator*() { return mCurrent; } \
\
MUTATION_GUARD(Inclusive##name_) \
\
protected: \
explicit Inclusive##name_(nsINode* aCurrent) : mCurrent(aCurrent) {} \
nsINode* mCurrent; \
}; \
class name_ : public Inclusive##name_ { \
using Super = Inclusive##name_; \
explicit name_(const nsINode& aNode) \
: Inclusive##name_(aNode.method_()) {} \
}; \
template <typename T> \
using name_##OfType = FilteredNodeIterator<T, name_>; \
template <typename T> \
using Inclusive##name_##OfType = FilteredNodeIterator<T, Inclusive##name_>;
DEFINE_ANCESTOR_ITERATOR(Ancestors, GetParentNode)
DEFINE_ANCESTOR_ITERATOR(FlatTreeAncestors, GetFlattenedTreeParentNode)
#undef MUTATION_GUARD
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_AncestorIterator.h

View File

@ -91,7 +91,12 @@ NS_INTERFACE_TABLE_HEAD(Attr)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Attr)
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Attr, LastRelease())
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(Attr,
LastRelease(),
Destroy())
NS_IMPL_DOMARENA_DESTROY(Attr)
void Attr::SetMap(nsDOMAttributeMap* aMap) {
if (mAttrMap && !aMap && sInitialized) {
@ -116,7 +121,7 @@ nsresult Attr::SetOwnerDocument(Document* aDocument) {
Document* doc = OwnerDoc();
NS_ASSERTION(doc != aDocument, "bad call to Attr::SetOwnerDocument");
doc->DeleteAllPropertiesFor(this);
doc->RemoveAllPropertiesFor(this);
RefPtr<dom::NodeInfo> newNodeInfo = aDocument->NodeInfoManager()->GetNodeInfo(
mNodeInfo->NameAtom(), mNodeInfo->GetPrefixAtom(),
@ -171,8 +176,9 @@ void Attr::SetNodeValueInternal(const nsAString& aNodeValue,
nsresult Attr::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
nsAutoString value;
const_cast<Attr*>(this)->GetValue(value);
*aResult = new (aNodeInfo->NodeInfoManager())
Attr(nullptr, do_AddRef(aNodeInfo), value);
*aResult = new Attr(nullptr, do_AddRef(aNodeInfo), value);
NS_ADDREF(*aResult);
return NS_OK;

View File

@ -26,13 +26,15 @@ class Document;
// Attribute helper class used to wrap up an attribute with a dom
// object that implements the DOM Attr interface.
class Attr final : public nsINode {
virtual ~Attr() {}
virtual ~Attr() = default;
public:
Attr(nsDOMAttributeMap* aAttrMap, already_AddRefed<dom::NodeInfo>&& aNodeInfo,
const nsAString& aValue);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL_DELETECYCLECOLLECTABLE
NS_DECL_DOMARENA_DESTROY
NS_IMPL_FROMNODE_HELPER(Attr, IsAttr())

View File

@ -310,6 +310,7 @@ nsresult AttrArray::DoSetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet) {
RefPtr<nsMappedAttributes> mapped =
GetModifiableMapped(nullptr, nullptr, false);
mapped->DropStyleSheetReference();
mapped->SetStyleSheet(aSheet);
return MakeMappedUnique(mapped);

View File

@ -5,8 +5,8 @@
#include "mozilla/dom/BarProps.h"
#include "mozilla/dom/BarPropBinding.h"
#include "nsContentUtils.h"
#include "nsDocShell.h"
#include "nsGlobalWindow.h"
#include "nsIScrollable.h"
#include "nsIWebBrowserChrome.h"
namespace mozilla {
@ -17,7 +17,7 @@ namespace dom {
//
BarProp::BarProp(nsGlobalWindowInner* aWindow) : mDOMWindow(aWindow) {}
BarProp::~BarProp() {}
BarProp::~BarProp() = default;
nsPIDOMWindowInner* BarProp::GetParentObject() const { return mDOMWindow; }
@ -88,7 +88,7 @@ already_AddRefed<nsIWebBrowserChrome> BarProp::GetBrowserChrome() {
MenubarProp::MenubarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
MenubarProp::~MenubarProp() {}
MenubarProp::~MenubarProp() = default;
bool MenubarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_MENUBAR, aRv);
@ -106,7 +106,7 @@ void MenubarProp::SetVisible(bool aVisible, CallerType aCallerType,
ToolbarProp::ToolbarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
ToolbarProp::~ToolbarProp() {}
ToolbarProp::~ToolbarProp() = default;
bool ToolbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_TOOLBAR, aRv);
@ -125,7 +125,7 @@ void ToolbarProp::SetVisible(bool aVisible, CallerType aCallerType,
LocationbarProp::LocationbarProp(nsGlobalWindowInner* aWindow)
: BarProp(aWindow) {}
LocationbarProp::~LocationbarProp() {}
LocationbarProp::~LocationbarProp() = default;
bool LocationbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_LOCATIONBAR,
@ -145,7 +145,7 @@ void LocationbarProp::SetVisible(bool aVisible, CallerType aCallerType,
PersonalbarProp::PersonalbarProp(nsGlobalWindowInner* aWindow)
: BarProp(aWindow) {}
PersonalbarProp::~PersonalbarProp() {}
PersonalbarProp::~PersonalbarProp() = default;
bool PersonalbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR,
@ -164,7 +164,7 @@ void PersonalbarProp::SetVisible(bool aVisible, CallerType aCallerType,
StatusbarProp::StatusbarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
StatusbarProp::~StatusbarProp() {}
StatusbarProp::~StatusbarProp() = default;
bool StatusbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_STATUSBAR, aRv);
@ -183,60 +183,24 @@ void StatusbarProp::SetVisible(bool aVisible, CallerType aCallerType,
ScrollbarsProp::ScrollbarsProp(nsGlobalWindowInner* aWindow)
: BarProp(aWindow) {}
ScrollbarsProp::~ScrollbarsProp() {}
ScrollbarsProp::~ScrollbarsProp() = default;
bool ScrollbarsProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
if (!mDOMWindow) {
return true;
}
nsCOMPtr<nsIScrollable> scroller =
do_QueryInterface(mDOMWindow->GetDocShell());
if (!scroller) {
nsIDocShell* ds = mDOMWindow->GetDocShell();
if (!ds) {
return true;
}
int32_t prefValue;
scroller->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
&prefValue);
if (prefValue != nsIScrollable::Scrollbar_Never) {
return true;
}
scroller->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
&prefValue);
return prefValue != nsIScrollable::Scrollbar_Never;
ScrollbarPreference pref = nsDocShell::Cast(ds)->ScrollbarPreference();
return pref != ScrollbarPreference::Never;
}
void ScrollbarsProp::SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) {
if (aCallerType != CallerType::System) {
return;
}
/* Scrollbars, unlike the other barprops, implement visibility directly
rather than handing off to the superclass (and from there to the
chrome window) because scrollbar visibility uniquely applies only
to the window making the change (arguably. it does now, anyway.)
and because embedding apps have no interface for implementing this
themselves, and therefore the implementation must be internal. */
nsContentUtils::SetScrollbarsVisibility(mDOMWindow->GetDocShell(), aVisible);
/* Notably absent is the part where we notify the chrome window using
GetBrowserChrome()->SetChromeFlags(). Given the possibility of multiple
DOM windows (multiple top-level windows, even) within a single
chrome window, the historical concept of a single "has scrollbars"
flag in the chrome is inapplicable, and we can't tell at this level
whether we represent the particular DOM window that makes this decision
for the chrome.
So only this object (and its corresponding DOM window) knows whether
scrollbars are visible. The corresponding chrome window will need to
ask (one of) its DOM window(s) when it needs to know about scrollbar
visibility, rather than caching its own copy of that information.
*/
void ScrollbarsProp::SetVisible(bool aVisible, CallerType, ErrorResult&) {
/* Do nothing */
}
} // namespace dom

View File

@ -36,8 +36,7 @@ class BarProp : public nsISupports, public nsWrapperCache {
nsPIDOMWindowInner* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) = 0;
virtual void SetVisible(bool aVisible, CallerType aCallerType,
@ -61,9 +60,9 @@ class MenubarProp final : public BarProp {
explicit MenubarProp(nsGlobalWindowInner* aWindow);
virtual ~MenubarProp();
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
virtual void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
};
// Script "toolbar" object
@ -72,9 +71,9 @@ class ToolbarProp final : public BarProp {
explicit ToolbarProp(nsGlobalWindowInner* aWindow);
virtual ~ToolbarProp();
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
virtual void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
};
// Script "locationbar" object
@ -83,9 +82,9 @@ class LocationbarProp final : public BarProp {
explicit LocationbarProp(nsGlobalWindowInner* aWindow);
virtual ~LocationbarProp();
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
virtual void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
};
// Script "personalbar" object
@ -94,9 +93,9 @@ class PersonalbarProp final : public BarProp {
explicit PersonalbarProp(nsGlobalWindowInner* aWindow);
virtual ~PersonalbarProp();
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
virtual void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
};
// Script "statusbar" object
@ -105,9 +104,9 @@ class StatusbarProp final : public BarProp {
explicit StatusbarProp(nsGlobalWindowInner* aWindow);
virtual ~StatusbarProp();
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
virtual void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
};
// Script "scrollbars" object
@ -116,9 +115,9 @@ class ScrollbarsProp final : public BarProp {
explicit ScrollbarsProp(nsGlobalWindowInner* aWindow);
virtual ~ScrollbarsProp();
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
virtual void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
void SetVisible(bool aVisible, CallerType aCallerType,
ErrorResult& aRv) override;
};
} // namespace dom

View File

@ -59,10 +59,7 @@ struct MOZ_STACK_CLASS BindContext final {
: nullptr),
mInComposedDoc(aParent.IsInComposedDoc()),
mInUncomposedDoc(aParent.IsInUncomposedDoc()),
mSubtreeRootChanges(true),
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aParent)) {}
mSubtreeRootChanges(true) {}
// When re-binding a shadow host into a tree, we re-bind all the shadow tree
// from the root. In that case, the shadow tree contents remain within the
@ -75,10 +72,7 @@ struct MOZ_STACK_CLASS BindContext final {
mBindingParent(aShadowRoot.Host()),
mInComposedDoc(aShadowRoot.IsInComposedDoc()),
mInUncomposedDoc(false),
mSubtreeRootChanges(false),
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aShadowRoot)) {}
mSubtreeRootChanges(false) {}
// This constructor is meant to be used when inserting native-anonymous
// children into a subtree.
@ -88,10 +82,7 @@ struct MOZ_STACK_CLASS BindContext final {
mBindingParent(&aParentElement),
mInComposedDoc(aParentElement.IsInComposedDoc()),
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
mSubtreeRootChanges(true),
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aParentElement)) {
mSubtreeRootChanges(true) {
MOZ_ASSERT(mInComposedDoc, "Binding NAC in a disconnected subtree?");
}
@ -101,27 +92,13 @@ struct MOZ_STACK_CLASS BindContext final {
mBindingParent(aBinding.GetBoundElement()),
mInComposedDoc(aParentElement.IsInComposedDoc()),
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
mSubtreeRootChanges(true),
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aParentElement)) {}
bool CollectingDisplayedNodeDataDuringLoad() const {
return mCollectingDisplayedNodeDataDuringLoad;
}
mSubtreeRootChanges(true) {}
private:
static bool IsLikelyUndisplayed(const nsINode& aParent) {
return aParent.IsAnyOfHTMLElements(nsGkAtoms::style, nsGkAtoms::script);
}
static bool ShouldCollectDisplayedNodeDataDuringLoad(bool aConnected,
Document& aDoc,
nsINode& aParent) {
return aDoc.GetReadyStateEnum() == Document::READYSTATE_LOADING &&
aConnected && !IsLikelyUndisplayed(aParent);
}
Document& mDoc;
Element* const mBindingParent;
@ -132,27 +109,6 @@ struct MOZ_STACK_CLASS BindContext final {
// Whether the bind operation will change the subtree root of the content
// we're binding.
const bool mSubtreeRootChanges;
// Whether it's likely that we're in an undisplayed part of the DOM.
//
// NOTE(emilio): We don't inherit this in BindContext's for Shadow DOM or XBL
// or such. This means that if you have a shadow tree inside an undisplayed
// element it will be incorrectly counted. But given our current definition
// of undisplayed element this is not likely to matter in practice.
bool mCollectingDisplayedNodeDataDuringLoad;
};
struct MOZ_STACK_CLASS BindContext::NestingLevel {
explicit NestingLevel(BindContext& aContext, const Element& aParent)
: mRestoreCollecting(aContext.mCollectingDisplayedNodeDataDuringLoad) {
if (aContext.mCollectingDisplayedNodeDataDuringLoad) {
aContext.mCollectingDisplayedNodeDataDuringLoad =
BindContext::IsLikelyUndisplayed(aParent);
}
}
private:
AutoRestore<bool> mRestoreCollecting;
};
} // namespace dom

View File

@ -735,7 +735,7 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
error.WouldReportJSException();
if (error.Failed()) {
localPromise->MaybeReject(error);
localPromise->MaybeReject(std::move(error));
}
}

View File

@ -318,7 +318,7 @@ BodyStream::BodyStream(nsIGlobalObject* aGlobal,
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
}
BodyStream::~BodyStream() {}
BodyStream::~BodyStream() = default;
void BodyStream::ErrorPropagation(JSContext* aCx,
const MutexAutoLock& aProofOfLock,
@ -337,10 +337,16 @@ void BodyStream::ErrorPropagation(JSContext* aCx,
}
// Let's use a generic error.
RefPtr<DOMException> error = DOMException::Create(NS_ERROR_DOM_TYPE_ERR);
ErrorResult rv;
// XXXbz can we come up with a better error message here to tell the
// consumer what went wrong?
rv.ThrowTypeError("Error in body stream");
JS::Rooted<JS::Value> errorValue(aCx);
if (ToJSValue(aCx, error, &errorValue)) {
bool ok = ToJSValue(aCx, std::move(rv), &errorValue);
MOZ_RELEASE_ASSERT(ok, "ToJSValue never fails for ErrorResult");
{
MutexAutoUnlock unlock(mMutex);
JS::ReadableStreamError(aCx, aStream, errorValue);
}

View File

@ -458,13 +458,8 @@ nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) {
aParent.AsContent()->GetContainingShadow();
}
if (IsInComposedDoc()) {
if (mText.IsBidi()) {
aContext.OwnerDoc().SetBidiEnabled();
}
if (aContext.CollectingDisplayedNodeDataDuringLoad()) {
aContext.OwnerDoc().AddToVisibleContentHeuristic(mText.GetLength());
}
if (IsInComposedDoc() && mText.IsBidi()) {
aContext.OwnerDoc().SetBidiEnabled();
}
// Clear the lazy frame construction bits.

View File

@ -310,7 +310,7 @@ class MOZ_NEEDS_MEMMOVABLE_MEMBERS StyleChildrenIterator
return *this;
}
~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
MOZ_COUNTED_DTOR(StyleChildrenIterator)
using AllChildrenIterator::GetNextChild;
using AllChildrenIterator::GetPreviousChild;

View File

@ -25,7 +25,7 @@ void ChromeNodeList::Append(nsINode& aNode, ErrorResult& aError) {
if (!aNode.IsContent()) {
// nsINodeList deals with nsIContent objects only, so need to
// filter out other nodes for now.
aError.Throw(NS_ERROR_DOM_TYPE_ERR);
aError.ThrowTypeError("The node passed in is not a ChildNode");
return;
}
@ -34,7 +34,7 @@ void ChromeNodeList::Append(nsINode& aNode, ErrorResult& aError) {
void ChromeNodeList::Remove(nsINode& aNode, ErrorResult& aError) {
if (!aNode.IsContent()) {
aError.Throw(NS_ERROR_DOM_TYPE_ERR);
aError.ThrowTypeError("The node passed in is not a ChildNode");
return;
}

View File

@ -83,12 +83,12 @@ void ChromeUtils::Base64URLEncode(GlobalObject& aGlobal,
uint8_t* data = nullptr;
if (aSource.IsArrayBuffer()) {
const ArrayBuffer& buffer = aSource.GetAsArrayBuffer();
buffer.ComputeLengthAndData();
buffer.ComputeState();
length = buffer.Length();
data = buffer.Data();
} else if (aSource.IsArrayBufferView()) {
const ArrayBufferView& view = aSource.GetAsArrayBufferView();
view.ComputeLengthAndData();
view.ComputeState();
length = view.Length();
data = view.Data();
} else {
@ -774,7 +774,7 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
thread->mCpuKernel = entry.cpuKernel;
thread->mTid = entry.tid;
}
procInfo.mThreads = threads;
procInfo.mThreads = std::move(threads);
mozilla::dom::Sequence<mozilla::dom::ChildProcInfoDictionary>
children;
@ -810,9 +810,9 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
thread->mTid = entry.tid;
thread->mName.Assign(entry.name);
}
childProcInfo->mThreads = threads;
childProcInfo->mThreads = std::move(threads);
}
procInfo.mChildren = children;
procInfo.mChildren = std::move(children);
domPromise->MaybeResolve(procInfo);
}; // end of ProcInfoResolver

View File

@ -17,12 +17,13 @@ using namespace dom;
namespace mozilla {
namespace dom {
Comment::~Comment() {}
Comment::~Comment() = default;
already_AddRefed<CharacterData> Comment::CloneDataNode(
mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const {
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
RefPtr<Comment> it = new Comment(ni.forget());
auto* nim = ni->NodeInfoManager();
RefPtr<Comment> it = new (nim) Comment(ni.forget());
if (aCloneText) {
it->mText = mText;
}

View File

@ -29,7 +29,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Crypto, mParent, mSubtle)
Crypto::Crypto(nsIGlobalObject* aParent) : mParent(aParent) {}
Crypto::~Crypto() {}
Crypto::~Crypto() = default;
/* virtual */
JSObject* Crypto::WrapObject(JSContext* aCx,
@ -58,7 +58,7 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
return;
}
aArray.ComputeLengthAndData();
aArray.ComputeState();
uint32_t dataLen = aArray.Length();
if (dataLen == 0) {
NS_WARNING("ArrayBufferView length is 0, cannot continue");

View File

@ -656,8 +656,7 @@ bool CustomElementRegistry::JSObjectToAtomArray(
if (!iterable.isUndefined()) {
if (!iterable.isObject()) {
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
NS_LITERAL_STRING("CustomElementRegistry.define: ") + aName);
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_ConvertUTF16toUTF8(aName));
return false;
}
@ -668,8 +667,7 @@ bool CustomElementRegistry::JSObjectToAtomArray(
}
if (!iter.valueIsIterable()) {
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
NS_LITERAL_STRING("CustomElementRegistry.define: ") + aName);
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_ConvertUTF16toUTF8(aName));
return false;
}
@ -691,10 +689,9 @@ bool CustomElementRegistry::JSObjectToAtomArray(
return false;
}
if (!aArray.AppendElement(NS_Atomize(attrStr))) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return false;
}
// XXX(Bug 1631371) Check if this should use a fallible operation as it
// pretended earlier.
aArray.AppendElement(NS_Atomize(attrStr));
}
}
@ -728,8 +725,7 @@ void CustomElementRegistry::Define(
* these steps.
*/
if (!JS::IsConstructor(constructorUnwrapped)) {
aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(
NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define"));
aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>("Argument 2");
return;
}
@ -868,8 +864,7 @@ void CustomElementRegistry::Define(
* 14.2. If Type(prototype) is not Object, then throw a TypeError exception.
*/
if (!prototype.isObject()) {
aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING(
"CustomElementRegistry.define: constructor.prototype"));
aRv.ThrowTypeError<MSG_NOT_OBJECT>("constructor.prototype");
return;
}
@ -961,7 +956,7 @@ void CustomElementRegistry::Define(
disableInternals, disableShadow);
CustomElementDefinition* def = definition.get();
mCustomDefinitions.Put(nameAtom, definition.forget());
mCustomDefinitions.Put(nameAtom, std::move(definition));
MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
"Number of entries should be the same");
@ -1020,7 +1015,7 @@ void CustomElementRegistry::SetElementCreationCallback(
}
RefPtr<CustomElementCreationCallback> callback = &aCallback;
mElementCreationCallbacks.Put(nameAtom, callback.forget());
mElementCreationCallbacks.Put(nameAtom, std::move(callback));
}
void CustomElementRegistry::Upgrade(nsINode& aRoot) {
@ -1119,7 +1114,7 @@ static void DoUpgrade(Element* aElement, CustomElementDefinition* aDefinition,
// always forms the return value from a JSObject.
if (NS_FAILED(UNWRAP_OBJECT(Element, &constructResult, element)) ||
element != aElement) {
aRv.ThrowTypeError(u"Custom element constructor returned a wrong element");
aRv.ThrowTypeError("Custom element constructor returned a wrong element");
return;
}
}

View File

@ -117,7 +117,7 @@ struct CustomElementData {
}
private:
virtual ~CustomElementData() {}
virtual ~CustomElementData() = default;
// Custom element type, for <button is="x-button"> or <x-button>
// this would be x-button.
@ -193,7 +193,7 @@ struct CustomElementDefinition {
}
private:
~CustomElementDefinition() {}
~CustomElementDefinition() = default;
};
class CustomElementReaction {
@ -290,7 +290,8 @@ class CustomElementReactionsStack {
}
private:
~CustomElementReactionsStack(){};
~CustomElementReactionsStack() = default;
;
/**
* Push a new element queue onto the custom element reactions stack.

52
dom/base/DOMArena.h Normal file
View File

@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef DOM_Arena_h___
#define DOM_Arena_h___
#include "nsISupportsImpl.h"
#include "mozmemory.h"
#define NS_DECL_DOMARENA_DESTROY void Destroy(void);
#define NS_IMPL_DOMARENA_DESTROY(class) \
void class ::Destroy(void) { \
if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) { \
RefPtr<nsNodeInfoManager> nim = OwnerDoc()->NodeInfoManager(); \
RefPtr<DOMArena> arena = \
HasFlag(NODE_KEEPS_DOMARENA) \
? nsContentUtils::TakeEntryFromDOMArenaTable(this) \
: nullptr; \
this->~class(); \
MOZ_ASSERT(nim, "nsNodeInfoManager needs to be initialized"); \
nim->Free(this); \
} else { \
delete this; \
} \
}
namespace mozilla {
namespace dom {
class DOMArena {
public:
friend class DocGroup;
DOMArena() { mArenaId = moz_create_arena(); }
NS_INLINE_DECL_REFCOUNTING(DOMArena)
void* Allocate(size_t aSize) {
void* ret = moz_arena_malloc(mArenaId, aSize);
if (!ret) {
MOZ_CRASH("run out of memory");
}
return ret;
}
private:
~DOMArena() { moz_dispose_arena(mArenaId); }
arena_id_t mArenaId;
};
} // namespace dom
} // namespace mozilla
#endif

View File

@ -168,7 +168,7 @@ class DOMException : public Exception {
const nsACString& aMessage);
protected:
virtual ~DOMException() {}
virtual ~DOMException() = default;
uint16_t mCode;
};

View File

@ -158,7 +158,8 @@ nsresult DOMImplementation::CreateHTMLDocument(const nsAString& aTitle,
rv = head->AppendChildTo(title, false);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsTextNode> titleText = new nsTextNode(doc->NodeInfoManager());
RefPtr<nsTextNode> titleText =
new (doc->NodeInfoManager()) nsTextNode(doc->NodeInfoManager());
rv = titleText->SetText(aTitle, false);
NS_ENSURE_SUCCESS(rv, rv);
rv = title->AppendChildTo(titleText, false);

View File

@ -23,7 +23,7 @@ class Document;
class DocumentType;
class DOMImplementation final : public nsISupports, public nsWrapperCache {
~DOMImplementation() {}
~DOMImplementation() = default;
public:
DOMImplementation(Document* aOwner, nsIGlobalObject* aScriptObject,

View File

@ -90,7 +90,7 @@ already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
observer->mThresholds.SetCapacity(thresholds.Length());
for (const auto& thresh : thresholds) {
if (thresh < 0.0 || thresh > 1.0) {
aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
aRv.ThrowRangeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
return nullptr;
}
observer->mThresholds.AppendElement(thresh);
@ -99,7 +99,7 @@ already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
} else {
double thresh = aOptions.mThreshold.GetAsDouble();
if (thresh < 0.0 || thresh > 1.0) {
aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
aRv.ThrowRangeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
return nullptr;
}
observer->mThresholds.AppendElement(thresh);

View File

@ -17,7 +17,7 @@ class DOMIntersectionObserver;
class DOMIntersectionObserverEntry final : public nsISupports,
public nsWrapperCache {
~DOMIntersectionObserverEntry() {}
~DOMIntersectionObserverEntry() = default;
public:
DOMIntersectionObserverEntry(nsISupports* aOwner, DOMHighResTimeStamp aTime,

View File

@ -58,8 +58,7 @@ static bool ValidateAndFixupMatrix2DInit(DOMMatrix2DInit& aMatrixInit,
} \
}
#define ValidateAndSet(field, alias, fieldName, aliasName, defaultValue) \
ValidateAliases((field), (alias), NS_LITERAL_STRING(fieldName), \
NS_LITERAL_STRING(aliasName)); \
ValidateAliases((field), (alias), fieldName, aliasName); \
SetFromAliasOrDefault((field), (alias), (defaultValue));
ValidateAndSet(aMatrixInit.mM11, aMatrixInit.mA, "m11", "a", 1);
@ -79,17 +78,16 @@ static bool ValidateAndFixupMatrix2DInit(DOMMatrix2DInit& aMatrixInit,
// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
static bool ValidateAndFixupMatrixInit(DOMMatrixInit& aMatrixInit,
ErrorResult& aRv) {
#define Check3DField(field, fieldName, defaultValue) \
if ((field) != (defaultValue)) { \
if (!aMatrixInit.mIs2D.WasPassed()) { \
aMatrixInit.mIs2D.Construct(false); \
return true; \
} \
if (aMatrixInit.mIs2D.Value()) { \
aRv.ThrowTypeError<MSG_MATRIX_INIT_EXCEEDS_2D>( \
NS_LITERAL_STRING(fieldName)); \
return false; \
} \
#define Check3DField(field, fieldName, defaultValue) \
if ((field) != (defaultValue)) { \
if (!aMatrixInit.mIs2D.WasPassed()) { \
aMatrixInit.mIs2D.Construct(false); \
return true; \
} \
if (aMatrixInit.mIs2D.Value()) { \
aRv.ThrowTypeError<MSG_MATRIX_INIT_EXCEEDS_2D>(fieldName); \
return false; \
} \
}
if (!ValidateAndFixupMatrix2DInit(aMatrixInit, aRv)) {
@ -190,7 +188,7 @@ already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromMatrix(
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
const GlobalObject& aGlobal, const Float32Array& aArray32,
ErrorResult& aRv) {
aArray32.ComputeLengthAndData();
aArray32.ComputeState();
const int length = aArray32.Length();
const bool is2D = length == 6;
@ -204,7 +202,7 @@ already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat64Array(
const GlobalObject& aGlobal, const Float64Array& aArray64,
ErrorResult& aRv) {
aArray64.ComputeLengthAndData();
aArray64.ComputeState();
const int length = aArray64.Length();
const bool is2D = length == 6;
@ -637,7 +635,7 @@ already_AddRefed<DOMMatrix> DOMMatrix::FromMatrix(
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
const GlobalObject& aGlobal, const Float32Array& aArray32,
ErrorResult& aRv) {
aArray32.ComputeLengthAndData();
aArray32.ComputeState();
const int length = aArray32.Length();
const bool is2D = length == 6;
@ -650,7 +648,7 @@ already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat64Array(
const GlobalObject& aGlobal, const Float64Array& aArray64,
ErrorResult& aRv) {
aArray64.ComputeLengthAndData();
aArray64.ComputeState();
const int length = aArray64.Length();
const bool is2D = length == 6;
@ -724,7 +722,7 @@ static void SetDataInMatrix(DOMMatrixReadOnly* aMatrix, const T* aData,
aMatrix->SetE(aData[4]);
aMatrix->SetF(aData[5]);
} else {
nsAutoString lengthStr;
nsAutoCString lengthStr;
lengthStr.AppendInt(aLength);
aRv.ThrowTypeError<MSG_MATRIX_INIT_LENGTH_WRONG>(lengthStr);
}

View File

@ -228,7 +228,7 @@ class DOMMatrixReadOnly : public nsWrapperCache {
nsAutoPtr<gfx::MatrixDouble> mMatrix2D;
nsAutoPtr<gfx::Matrix4x4Double> mMatrix3D;
virtual ~DOMMatrixReadOnly() {}
virtual ~DOMMatrixReadOnly() = default;
/**
* Sets data from a fully validated and fixed-up matrix init,
@ -317,7 +317,7 @@ class DOMMatrix : public DOMMatrixReadOnly {
DOMMatrix* InvertSelf();
DOMMatrix* SetMatrixValue(const nsACString&, ErrorResult&);
virtual ~DOMMatrix() {}
virtual ~DOMMatrix() = default;
private:
DOMMatrix(nsISupports* aParent, bool is2D)

View File

@ -37,7 +37,7 @@ DOMParser::DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal,
MOZ_ASSERT(aDocumentURI);
}
DOMParser::~DOMParser() {}
DOMParser::~DOMParser() = default;
// QueryInterface implementation for DOMParser
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser)
@ -116,7 +116,7 @@ already_AddRefed<Document> DOMParser::ParseFromSafeString(const nsAString& aStr,
already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
SupportedType aType,
ErrorResult& aRv) {
aBuf.ComputeLengthAndData();
aBuf.ComputeState();
return ParseFromBuffer(MakeSpan(aBuf.Data(), aBuf.Length()), aType, aRv);
}

View File

@ -57,7 +57,7 @@ class DOMPointReadOnly : public nsWrapperCache {
JSStructuredCloneReader* aReader);
protected:
virtual ~DOMPointReadOnly() {}
virtual ~DOMPointReadOnly() = default;
// Shared implementation of ReadStructuredClone for DOMPoint and
// DOMPointReadOnly.

View File

@ -28,7 +28,7 @@ DOMQuad::DOMQuad(nsISupports* aParent, CSSPoint aPoints[4]) : mParent(aParent) {
DOMQuad::DOMQuad(nsISupports* aParent) : mParent(aParent) {}
DOMQuad::~DOMQuad() {}
DOMQuad::~DOMQuad() = default;
JSObject* DOMQuad::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {

View File

@ -25,7 +25,7 @@ struct DOMRectInit;
class DOMRectReadOnly : public nsISupports, public nsWrapperCache {
protected:
virtual ~DOMRectReadOnly() {}
virtual ~DOMRectReadOnly() = default;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -129,11 +129,11 @@ class DOMRect final : public DOMRectReadOnly {
}
private:
~DOMRect() {}
~DOMRect() = default;
};
class DOMRectList final : public nsISupports, public nsWrapperCache {
~DOMRectList() {}
~DOMRectList() = default;
public:
explicit DOMRectList(nsISupports* aParent) : mParent(aParent) {}

View File

@ -81,7 +81,7 @@ class DOMRequest : public DOMEventTargetHelper {
};
class DOMRequestService final : public nsIDOMRequestService {
~DOMRequestService() {}
~DOMRequestService() = default;
public:
NS_DECL_ISUPPORTS

View File

@ -18,7 +18,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMStringList)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
DOMStringList::~DOMStringList() {}
DOMStringList::~DOMStringList() = default;
JSObject* DOMStringList::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {

View File

@ -55,9 +55,11 @@ class DOMStringList : public nsISupports, public nsWrapperCache {
}
bool Add(const nsAString& aName) {
// XXXbz mNames should really be a fallible array; otherwise this
// return value is meaningless.
return mNames.AppendElement(aName) != nullptr;
// XXXbz(Bug 1631374) mNames should really be a fallible array; otherwise
// this return value is meaningless. return mNames.AppendElement(aName) !=
// nullptr;
mNames.AppendElement(aName);
return true;
}
void Clear() { mNames.Clear(); }

View File

@ -274,11 +274,14 @@ static bool AncestorChainCrossesShadowBoundary(nsIContent* aDescendant,
* test for it separately, e.g. with DoesNotAffectDirectionOfAncestors.
* It *does* include textarea, because even if a textarea has dir=auto, it has
* unicode-bidi: plaintext and is handled automatically in bidi resolution.
* It also includes `input`, because it takes the `dir` value from its value
* attribute, instead of the child nodes.
*/
static bool DoesNotParticipateInAutoDirection(const nsIContent* aContent) {
mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo();
return ((!aContent->IsHTMLElement() || nodeInfo->Equals(nsGkAtoms::script) ||
nodeInfo->Equals(nsGkAtoms::style) ||
nodeInfo->Equals(nsGkAtoms::input) ||
nodeInfo->Equals(nsGkAtoms::textarea) ||
aContent->IsInAnonymousSubtree())) &&
!aContent->IsShadowRoot();
@ -506,9 +509,7 @@ class nsTextNodeDirectionalityMap {
aTextNode->SetHasTextNodeDirectionalityMap();
}
~nsTextNodeDirectionalityMap() {
MOZ_COUNT_DTOR(nsTextNodeDirectionalityMap);
}
MOZ_COUNTED_DTOR(nsTextNodeDirectionalityMap)
static void nsTextNodeDirectionalityMapPropertyDestructor(
void* aObject, nsAtom* aProperty, void* aPropertyValue, void* aData) {
@ -536,7 +537,7 @@ class nsTextNodeDirectionalityMap {
mElements.Remove(aElement);
aElement->ClearHasDirAutoSet();
aElement->DeleteProperty(nsGkAtoms::dirAutoSetBy);
aElement->RemoveProperty(nsGkAtoms::dirAutoSetBy);
}
void RemoveEntryForProperty(Element* aElement) {
@ -602,7 +603,7 @@ class nsTextNodeDirectionalityMap {
nsTextNodeDirectionalityMap::AddEntryToMap(newTextNode, rootNode);
} else {
rootNode->ClearHasDirAutoSet();
rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy);
rootNode->RemoveProperty(nsGkAtoms::dirAutoSetBy);
}
return OpRemove;
}
@ -631,7 +632,7 @@ class nsTextNodeDirectionalityMap {
mElements.EnumerateEntries(TakeEntries, &entries);
for (Element* el : entries) {
el->ClearHasDirAutoSet();
el->DeleteProperty(nsGkAtoms::dirAutoSetBy);
el->RemoveProperty(nsGkAtoms::dirAutoSetBy);
}
}

View File

@ -50,6 +50,10 @@ DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey)
: mKey(aKey), mTabGroup(aTabGroup) {
// This method does not add itself to mTabGroup->mDocGroups as the caller does
// it for us.
if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
mArena = new mozilla::dom::DOMArena();
}
mPerformanceCounter =
new mozilla::PerformanceCounter(NS_LITERAL_CSTRING("DocGroup:") + aKey);
}
@ -60,6 +64,8 @@ DocGroup::~DocGroup() {
nsIEventTarget* target = EventTargetFor(TaskCategory::Other);
NS_ProxyRelease("DocGroup::mReactionsStack", target,
mReactionsStack.forget());
NS_ProxyRelease("DocGroup::mArena", target, mArena.forget());
}
mTabGroup->mDocGroups.RemoveEntry(mKey);
@ -144,7 +150,7 @@ RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
->Then(
mainThread, __func__,
[self, host, pid, windowID, duration, isTopLevel,
items](const PerformanceMemoryInfo& aMemoryInfo) {
items = std::move(items)](const PerformanceMemoryInfo& aMemoryInfo) {
PerformanceInfo info =
PerformanceInfo(host, pid, windowID, duration,
self->mPerformanceCounter->GetID(), false,

View File

@ -56,6 +56,9 @@ class DocGroup final {
RefPtr<PerformanceInfoPromise> ReportPerformanceInfo();
TabGroup* GetTabGroup() { return mTabGroup; }
mozilla::dom::DOMArena* ArenaAllocator() { return mArena; }
mozilla::dom::CustomElementReactionsStack* CustomElementReactionsStack() {
MOZ_ASSERT(NS_IsMainThread());
if (!mReactionsStack) {
@ -124,6 +127,8 @@ class DocGroup final {
RefPtr<mozilla::ThrottledEventQueue> mIframePostMessageQueue;
nsTHashtable<nsUint64HashKey> mIframesUsedPostMessageQueue;
RefPtr<mozilla::dom::DOMArena> mArena;
};
} // namespace dom

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,15 @@
#include "mozilla/EventStates.h" // for EventStates
#include "mozilla/FlushType.h" // for enum
#include "mozilla/MozPromise.h" // for MozPromise
#include "mozilla/FunctionRef.h" // for FunctionRef
#include "mozilla/Pair.h" // for Pair
#include "nsAutoPtr.h" // for member
#include "nsCOMArray.h" // for member
#include "nsCompatibility.h" // for member
#include "nsCOMPtr.h" // for member
#include "nsICookieSettings.h"
#include "nsGkAtoms.h" // for static class members
#include "nsGkAtoms.h" // for static class members
#include "nsNameSpaceManager.h" // for static class members
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIContentViewer.h"
@ -144,6 +146,7 @@ class ServoStyleSet;
enum class StyleOrigin : uint8_t;
class SMILAnimationController;
enum class StyleCursorKind : uint8_t;
enum class StylePrefersColorScheme : uint8_t;
template <typename>
class OwningNonNull;
struct URLExtraData;
@ -284,7 +287,7 @@ class DocHeaderData {
};
class ExternalResourceMap {
typedef bool (*SubDocEnumFunc)(Document& aDocument, void* aData);
using SubDocEnumFunc = FunctionRef<bool(Document&)>;
public:
/**
@ -327,7 +330,7 @@ class ExternalResourceMap {
* Enumerate the resource documents. See
* Document::EnumerateExternalResources.
*/
void EnumerateResources(SubDocEnumFunc aCallback, void* aData);
void EnumerateResources(SubDocEnumFunc aCallback);
/**
* Traverse ourselves for cycle-collection
@ -362,7 +365,7 @@ class ExternalResourceMap {
protected:
class PendingLoad : public ExternalResourceLoad, public nsIStreamListener {
~PendingLoad() {}
~PendingLoad() = default;
public:
explicit PendingLoad(Document* aDisplayDocument)
@ -393,7 +396,7 @@ class ExternalResourceMap {
friend class PendingLoad;
class LoadgroupCallbacks final : public nsIInterfaceRequestor {
~LoadgroupCallbacks() {}
~LoadgroupCallbacks() = default;
public:
explicit LoadgroupCallbacks(nsIInterfaceRequestor* aOtherCallbacks)
@ -464,6 +467,8 @@ class Document : public nsINode,
public nsStubMutationObserver,
public DispatcherTrait,
public SupportsWeakPtr<Document> {
friend class DocumentOrShadowRoot;
protected:
explicit Document(const char* aContentType);
virtual ~Document();
@ -474,6 +479,12 @@ class Document : public nsINode,
public:
typedef dom::ExternalResourceMap::ExternalResourceLoad ExternalResourceLoad;
typedef dom::ReferrerPolicy ReferrerPolicyEnum;
using AdoptedStyleSheetCloneCache =
nsRefPtrHashtable<nsPtrHashKey<const StyleSheet>, StyleSheet>;
// nsINode overrides the new operator for DOM Arena allocation.
// to use the default one, we need to bring it back again
void* operator new(size_t aSize) { return ::operator new(aSize); }
/**
* Called when XPCOM shutdown.
@ -565,6 +576,8 @@ class Document : public nsINode,
return mIntrinsicStoragePrincipal;
}
void ClearActiveStoragePrincipal() { mActiveStoragePrincipal = nullptr; }
nsIPrincipal* GetContentBlockingAllowListPrincipal() const {
return mContentBlockingAllowListPrincipal;
}
@ -1103,6 +1116,14 @@ class Document : public nsINode,
mHasUnsafeEvalCSP = aHasUnsafeEvalCSP;
}
/**
* Returns true if the document holds a CSP
* delivered through an HTTP Header.
*/
bool GetHasCSPDeliveredThroughHeader() {
return mHasCSPDeliveredThroughHeader;
}
/**
* Get the content blocking log.
*/
@ -1779,7 +1800,7 @@ class Document : public nsINode,
nsExpirationState* GetExpirationState() { return &mState; }
~SelectorCacheKey() { MOZ_COUNT_DTOR(SelectorCacheKey); }
MOZ_COUNTED_DTOR(SelectorCacheKey)
};
class SelectorCacheKeyDeleter;
@ -1878,11 +1899,6 @@ class Document : public nsINode,
InsertSheetAt(SheetCount(), *aSheet);
}
/**
* Remove a stylesheet from the document
*/
void RemoveStyleSheet(StyleSheet&);
/**
* Notify the document that the applicable state of the sheet changed
* and that observers should be notified and style sets updated
@ -1906,8 +1922,6 @@ class Document : public nsINode,
return mAdditionalSheets[eAuthorSheet].SafeElementAt(0);
}
void AppendAdoptedStyleSheet(StyleSheet& aSheet);
/**
* Returns the index that aSheet should be inserted at to maintain document
* ordering.
@ -2028,14 +2042,14 @@ class Document : public nsINode,
void RemoveFromNameTable(Element* aElement, nsAtom* aName);
/**
* Returns all elements in the fullscreen stack in the insertion order.
* Returns all elements in the top layer in the insertion order.
*/
nsTArray<Element*> GetFullscreenStack() const;
nsTArray<Element*> GetTopLayer() const;
/**
* Asynchronously requests that the document make aElement the fullscreen
* element, and move into fullscreen mode. The current fullscreen element
* (if any) is pushed onto the fullscreen element stack, and it can be
* (if any) is pushed onto the top layer, and it can be
* returned to fullscreen status by calling RestorePreviousFullscreenState().
*
* Note that requesting fullscreen in a document also makes the element which
@ -2048,25 +2062,30 @@ class Document : public nsINode,
// Do the "fullscreen element ready check" from the fullscreen spec.
// It returns true if the given element is allowed to go into fullscreen.
// It is responsive to dispatch "fullscreenerror" event when necessary.
bool FullscreenElementReadyCheck(const FullscreenRequest&);
bool FullscreenElementReadyCheck(FullscreenRequest&);
// This is called asynchronously by Document::AsyncRequestFullscreen()
// to move this document into fullscreen mode if allowed.
void RequestFullscreen(UniquePtr<FullscreenRequest> aRequest);
// Removes all elements from the fullscreen stack, removing full-scren
// styles from the top element in the stack.
// Removes all the elements with fullscreen flag set from the top layer, and
// clears their fullscreen flag.
void CleanupFullscreenState();
// Pushes aElement onto the fullscreen stack, and removes fullscreen styles
// from the former fullscreen stack top, and its ancestors, and applies the
// styles to aElement. aElement becomes the new "fullscreen element".
bool FullscreenStackPush(Element* aElement);
// Pushes aElement onto the top layer
bool TopLayerPush(Element* aElement);
// Remove the top element from the fullscreen stack. Removes the fullscreen
// styles from the former top element, and applies them to the new top
// element, if there is one.
void FullscreenStackPop();
// Removes the topmost element which have aPredicate return true from the top
// layer. The removed element, if any, is returned.
Element* TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc);
// Pops the fullscreen element from the top layer and clears its
// fullscreen flag.
void UnsetFullscreenElement();
// Pushes the given element into the top of top layer and set fullscreen
// flag.
bool SetFullscreenElement(Element* aElement);
/**
* Called when a frame in a child process has entered fullscreen or when a
@ -2078,7 +2097,7 @@ class Document : public nsINode,
/**
* Called when a frame in a remote child document has rolled back fullscreen
* so that all its fullscreen element stacks are empty; we must continue the
* so that all its top layer are empty; we must continue the
* rollback in this parent process' doc tree branch which is fullscreen.
* Note that only one branch of the document tree can have its documents in
* fullscreen state at one time. We're in inconsistent state if a
@ -2107,6 +2126,8 @@ class Document : public nsINode,
*/
Document* GetFullscreenRoot();
size_t CountFullscreenElements() const;
/**
* Sets the fullscreen root to aRoot. This stores a weak reference to aRoot
* in this document.
@ -2412,8 +2433,8 @@ class Document : public nsINode,
*/
int32_t GetDefaultNamespaceID() const { return mDefaultElementType; }
void DeleteAllProperties();
void DeleteAllPropertiesFor(nsINode* aNode);
void RemoveAllProperties();
void RemoveAllPropertiesFor(nsINode* aNode);
nsPropertyTable& PropertyTable() { return mPropertyTable; }
@ -2438,8 +2459,8 @@ class Document : public nsINode,
* The enumerator callback should return true to continue enumerating, or
* false to stop. This will never get passed a null aDocument.
*/
typedef bool (*SubDocEnumFunc)(Document&, void* aData);
void EnumerateSubDocuments(SubDocEnumFunc aCallback, void* aData);
using SubDocEnumFunc = FunctionRef<bool(Document&)>;
void EnumerateSubDocuments(SubDocEnumFunc aCallback);
/**
* Collect all the descendant documents for which |aCalback| returns true.
@ -2768,7 +2789,7 @@ class Document : public nsINode,
* The enumerator callback should return true to continue enumerating, or
* false to stop. This callback will never get passed a null aDocument.
*/
void EnumerateExternalResources(SubDocEnumFunc aCallback, void* aData);
void EnumerateExternalResources(SubDocEnumFunc aCallback);
dom::ExternalResourceMap& ExternalResourceMap() {
return mExternalResourceMap;
@ -2836,9 +2857,8 @@ class Document : public nsINode,
void RegisterActivityObserver(nsISupports* aSupports);
bool UnregisterActivityObserver(nsISupports* aSupports);
// Enumerate all the observers in mActivityObservers by the aEnumerator.
typedef void (*ActivityObserverEnumerator)(nsISupports*, void*);
void EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
void* aData);
using ActivityObserverEnumerator = FunctionRef<void(nsISupports*)>;
void EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator);
// Indicates whether mAnimationController has been (lazily) initialized.
// If this returns true, we're promising that GetAnimationController()
@ -2986,7 +3006,7 @@ class Document : public nsINode,
* Note that static documents are also "loaded as data" (if this method
* returns true, IsLoadedAsData() will also return true).
*/
bool IsStaticDocument() { return mIsStaticDocument; }
bool IsStaticDocument() const { return mIsStaticDocument; }
/**
* Clones the document along with any subdocuments, stylesheet, etc.
@ -3490,6 +3510,7 @@ class Document : public nsINode,
mozilla::ErrorResult& rv);
Nullable<WindowProxyHolder> GetDefaultView() const;
Element* GetActiveElement();
nsIContent* GetUnretargetedFocusedContent() const;
bool HasFocus(ErrorResult& rv) const;
void GetDesignMode(nsAString& aDesignMode);
void SetDesignMode(const nsAString& aDesignMode,
@ -3526,7 +3547,9 @@ class Document : public nsINode,
nsIURI* GetDocumentURIObject() const;
// Not const because all the fullscreen goop is not const
bool FullscreenEnabled(CallerType aCallerType);
Element* FullscreenStackTop();
Element* GetTopLayerTop();
// Return the fullscreen element in the top layer
Element* GetUnretargetedFullScreenElement();
bool Fullscreen() { return !!GetFullscreenElement(); }
already_AddRefed<Promise> ExitFullscreen(ErrorResult&);
void ExitPointerLock() { UnlockPointer(this); }
@ -3718,16 +3741,6 @@ class Document : public nsINode,
bool IsSynthesized();
void AddToVisibleContentHeuristic(size_t aNumber) {
if (MOZ_UNLIKELY(SIZE_MAX - mVisibleContentHeuristic < aNumber)) {
mVisibleContentHeuristic = SIZE_MAX;
} else {
mVisibleContentHeuristic += aNumber;
}
}
size_t GetVisibleContentHeuristic() const { return mVisibleContentHeuristic; }
// Called to track whether this document has had any interaction.
// This is used to track whether we should permit "beforeunload".
void SetUserHasInteracted();
@ -3777,6 +3790,8 @@ class Document : public nsINode,
return mDocGroup;
}
DocGroup* GetDocGroupOrCreate();
/**
* If we're a sub-document, the parent document's layout can affect our style
* and layout (due to the viewport size, viewport units, media queries...).
@ -4052,6 +4067,9 @@ class Document : public nsINode,
bool InRDMPane() const { return mInRDMPane; }
void SetInRDMPane(bool aInRDMPane) { mInRDMPane = aInRDMPane; }
// CSS prefers-color-scheme media feature for this document.
StylePrefersColorScheme PrefersColorScheme() const;
// Returns true if we use overlay scrollbars on the system wide or on the
// given document.
static bool UseOverlayScrollbars(const Document* aDocument);
@ -4060,10 +4078,6 @@ class Document : public nsINode,
static bool AutomaticStorageAccessCanBeGranted(nsIPrincipal* aPrincipal);
void SetAdoptedStyleSheets(
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
ErrorResult& aRv);
protected:
void DoUpdateSVGUseElementShadowTrees();
@ -4104,8 +4118,6 @@ class Document : public nsINode,
bool ApplyFullscreen(UniquePtr<FullscreenRequest>);
void RemoveDocStyleSheetsFromStyleSets();
void RemoveStyleSheetsFromStyleSets(
const nsTArray<RefPtr<StyleSheet>>& aSheets, StyleOrigin);
void ResetStylesheetsToURI(nsIURI* aURI);
void FillStyleSet();
void FillStyleSetUserAndUASheets();
@ -4118,8 +4130,8 @@ class Document : public nsINode,
}
void AddContentEditableStyleSheetsToStyleSet(bool aDesignMode);
void RemoveContentEditableStyleSheets();
void AddStyleSheetToStyleSets(StyleSheet* aSheet);
void RemoveStyleSheetFromStyleSets(StyleSheet* aSheet);
void AddStyleSheetToStyleSets(StyleSheet&);
void RemoveStyleSheetFromStyleSets(StyleSheet&);
void NotifyStyleSheetApplicableStateChanged();
// Just like EnableStyleSheetsForSet, but doesn't check whether
// aSheetSet is null and allows the caller to control whether to set
@ -4564,6 +4576,9 @@ class Document : public nsINode,
// True if a document load has a CSP with unsafe-inline attached.
bool mHasUnsafeInlineCSP : 1;
// True if the document has a CSP delivered throuh a header
bool mHasCSPDeliveredThroughHeader : 1;
// True if DisallowBFCaching has been called on this document.
bool mBFCacheDisallowed : 1;
@ -4917,16 +4932,6 @@ class Document : public nsINode,
// Array of observers
nsTObserverArray<nsIDocumentObserver*> mObservers;
// An ever-increasing heuristic number that is higher the more content is
// likely to be visible in the page.
//
// Right now it effectively measures amount of text content that has ever been
// connected to the document in some way, and is not under a <script> or
// <style>.
//
// Note that this is only measured during load.
size_t mVisibleContentHeuristic = 0;
// Whether the user has interacted with the document or not:
bool mUserHasInteracted;
@ -5019,10 +5024,8 @@ class Document : public nsINode,
// Array of intersection observers
nsTHashtable<nsPtrHashKey<DOMIntersectionObserver>> mIntersectionObservers;
// Stack of fullscreen elements. When we request fullscreen we push the
// fullscreen element onto this stack, and when we cancel fullscreen we
// pop one off this stack, restoring the previous fullscreen state
nsTArray<nsWeakPtr> mFullscreenStack;
// Stack of top layer elements.
nsTArray<nsWeakPtr> mTopLayer;
// The root of the doc tree in which this document is in. This is only
// non-null when this document is in fullscreen mode.
@ -5170,6 +5173,11 @@ class Document : public nsINode,
// The principal to use for the storage area of this document.
nsCOMPtr<nsIPrincipal> mIntrinsicStoragePrincipal;
// The cached storage principal for this document.
// This is mutable so that we can keep EffectiveStoragePrincipal() const
// which is required due to its CloneDocHelper() call site. :-(
mutable nsCOMPtr<nsIPrincipal> mActiveStoragePrincipal;
// The principal to use for the content blocking allow list.
nsCOMPtr<nsIPrincipal> mContentBlockingAllowListPrincipal;

View File

@ -82,7 +82,7 @@ class DocumentFragment : public FragmentOrElement {
#endif
protected:
virtual ~DocumentFragment() {}
virtual ~DocumentFragment() = default;
nsresult Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
RefPtr<Element> mHost;

View File

@ -71,35 +71,44 @@ StyleSheetList* DocumentOrShadowRoot::StyleSheets() {
}
void DocumentOrShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
aSheet.SetAssociatedDocumentOrShadowRoot(
this, StyleSheet::OwnedByDocumentOrShadowRoot);
aSheet.SetAssociatedDocumentOrShadowRoot(this);
mStyleSheets.InsertElementAt(aIndex, &aSheet);
}
void DocumentOrShadowRoot::InsertAdoptedSheetAt(size_t aIndex,
StyleSheet& aSheet) {
mAdoptedStyleSheets.InsertElementAt(aIndex, &aSheet);
}
already_AddRefed<StyleSheet> DocumentOrShadowRoot::RemoveSheet(
StyleSheet& aSheet) {
void DocumentOrShadowRoot::RemoveStyleSheet(StyleSheet& aSheet) {
auto index = mStyleSheets.IndexOf(&aSheet);
if (index == mStyleSheets.NoIndex) {
return nullptr;
// We should only hit this case if we are unlinking
// in which case mStyleSheets should be cleared.
MOZ_ASSERT(mKind != Kind::Document ||
AsNode().AsDocument()->InUnlinkOrDeletion());
MOZ_ASSERT(mStyleSheets.IsEmpty());
return;
}
RefPtr<StyleSheet> sheet = std::move(mStyleSheets[index]);
mStyleSheets.RemoveElementAt(index);
RemoveSheetFromStylesIfApplicable(*sheet);
sheet->ClearAssociatedDocumentOrShadowRoot();
return sheet.forget();
}
void DocumentOrShadowRoot::RemoveSheetFromStylesIfApplicable(
StyleSheet& aSheet) {
if (!aSheet.IsApplicable()) {
return;
}
if (mKind == Kind::Document) {
AsNode().AsDocument()->RemoveStyleSheetFromStyleSets(aSheet);
} else {
MOZ_ASSERT(AsNode().IsShadowRoot());
static_cast<ShadowRoot&>(AsNode()).RemoveSheetFromStyles(aSheet);
}
}
// https://wicg.github.io/construct-stylesheets/#dom-documentorshadowroot-adoptedstylesheets
void DocumentOrShadowRoot::EnsureAdoptedSheetsAreValid(
void DocumentOrShadowRoot::SetAdoptedStyleSheets(
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
ErrorResult& aRv) {
nsTHashtable<nsPtrHashKey<const StyleSheet>> set(
aAdoptedStyleSheets.Length());
Document& doc = *AsNode().OwnerDoc();
for (const OwningNonNull<StyleSheet>& sheet : aAdoptedStyleSheets) {
// 2.1 Check if all sheets are constructed, else throw NotAllowedError
if (!sheet->IsConstructed()) {
@ -109,23 +118,117 @@ void DocumentOrShadowRoot::EnsureAdoptedSheetsAreValid(
}
// 2.2 Check if all sheets' constructor documents match the
// DocumentOrShadowRoot's node document, else throw NotAlloweError
if (!sheet->ConstructorDocumentMatches(AsNode().OwnerDoc())) {
if (!sheet->ConstructorDocumentMatches(doc)) {
return aRv.ThrowNotAllowedError(
"Each adopted style sheet's constructor document must match the "
"document or shadow root's node document");
}
}
// FIXME(nordzilla): This is temporary code to disallow duplicate sheets.
// This exists to ensure that the fuzzers aren't blocked.
// This code will eventually be removed.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1617302
if (!set.EnsureInserted(sheet.get())) {
return aRv.ThrowNotAllowedError(
"Temporarily disallowing duplicate stylesheets.");
auto* shadow = ShadowRoot::FromNode(AsNode());
MOZ_ASSERT((mKind == Kind::ShadowRoot) == !!shadow);
StyleSheetSet set(aAdoptedStyleSheets.Length());
size_t commonPrefix = 0;
// Find the index at which the new array differs from the old array.
// We don't want to do extra work for the sheets that both arrays have.
size_t min =
std::min(aAdoptedStyleSheets.Length(), mAdoptedStyleSheets.Length());
for (size_t i = 0; i < min; ++i) {
if (aAdoptedStyleSheets[i] != mAdoptedStyleSheets[i]) {
break;
}
++commonPrefix;
set.PutEntry(mAdoptedStyleSheets[i]);
}
// Try to truncate the sheets to a common prefix.
// If the prefix contains duplicates of sheets that we are removing,
// we are just going to re-build everything from scratch.
if (commonPrefix != mAdoptedStyleSheets.Length()) {
StyleSheetSet removedSet(mAdoptedStyleSheets.Length() - commonPrefix);
for (size_t i = mAdoptedStyleSheets.Length(); i != commonPrefix; --i) {
StyleSheet* sheetToRemove = mAdoptedStyleSheets.ElementAt(i - 1);
if (MOZ_UNLIKELY(set.Contains(sheetToRemove))) {
// Fixing duplicate sheets would require insertions/removals from the
// style set. We may as well just rebuild the whole thing from scratch.
set.Clear();
// Note that setting this to zero means we'll continue the loop until
// all the sheets are cleared.
commonPrefix = 0;
}
if (MOZ_LIKELY(removedSet.EnsureInserted(sheetToRemove))) {
RemoveSheetFromStylesIfApplicable(*sheetToRemove);
sheetToRemove->RemoveAdopter(*this);
}
}
mAdoptedStyleSheets.TruncateLength(commonPrefix);
}
// 3. Set the adopted style sheets to the new sheets
mAdoptedStyleSheets.SetCapacity(aAdoptedStyleSheets.Length());
// Only add sheets that are not already in the common prefix.
for (const auto& sheet : MakeSpan(aAdoptedStyleSheets).From(commonPrefix)) {
if (MOZ_UNLIKELY(!set.EnsureInserted(sheet))) {
// The idea is that this case is rare, so we pay the price of removing the
// old sheet from the styles and append it later rather than the other way
// around.
RemoveSheetFromStylesIfApplicable(*sheet);
} else {
sheet->AddAdopter(*this);
}
mAdoptedStyleSheets.AppendElement(sheet);
if (sheet->IsApplicable()) {
if (mKind == Kind::Document) {
doc.AddStyleSheetToStyleSets(*sheet);
} else {
shadow->InsertSheetIntoAuthorData(mAdoptedStyleSheets.Length() - 1,
*sheet, mAdoptedStyleSheets);
}
}
}
}
void DocumentOrShadowRoot::ClearAdoptedStyleSheets() {
EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
RemoveSheetFromStylesIfApplicable(aSheet);
aSheet.RemoveAdopter(*this);
});
mAdoptedStyleSheets.Clear();
}
void DocumentOrShadowRoot::CloneAdoptedSheetsFrom(
const DocumentOrShadowRoot& aSource) {
if (!aSource.AdoptedSheetCount()) {
return;
}
Sequence<OwningNonNull<StyleSheet>> list;
if (!list.SetCapacity(mAdoptedStyleSheets.Length(), fallible)) {
return;
}
Document& ownerDoc = *AsNode().OwnerDoc();
const Document& sourceDoc = *aSource.AsNode().OwnerDoc();
auto* clonedSheetMap = static_cast<Document::AdoptedStyleSheetCloneCache*>(
sourceDoc.GetProperty(nsGkAtoms::adoptedsheetclones));
MOZ_ASSERT(clonedSheetMap);
for (const StyleSheet* sheet : aSource.mAdoptedStyleSheets) {
RefPtr<StyleSheet> clone = clonedSheetMap->LookupForAdd(sheet).OrInsert(
[&] { return sheet->CloneAdoptedSheet(ownerDoc); });
MOZ_ASSERT(clone);
MOZ_DIAGNOSTIC_ASSERT(clone->ConstructorDocumentMatches(ownerDoc));
DebugOnly<bool> succeeded = list.AppendElement(std::move(clone), fallible);
MOZ_ASSERT(succeeded);
}
ErrorResult rv;
SetAdoptedStyleSheets(list, rv);
MOZ_ASSERT(!rv.Failed());
}
Element* DocumentOrShadowRoot::GetElementById(const nsAString& aElementId) {
if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
@ -184,25 +287,13 @@ nsIContent* DocumentOrShadowRoot::Retarget(nsIContent* aContent) const {
}
Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) {
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
window, nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedWindow));
// be safe and make sure the element is from this document
if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
if (focusedContent->ChromeOnlyAccess()) {
focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
}
if (focusedContent) {
if (nsIContent* retarget = Retarget(focusedContent)) {
return retarget->AsElement();
}
}
}
auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
if (!content) {
return nullptr;
}
if (nsIContent* retarget = Retarget(content)) {
return retarget->AsElement();
}
return nullptr;
}
@ -225,7 +316,7 @@ Element* DocumentOrShadowRoot::GetFullscreenElement() {
return nullptr;
}
Element* element = AsNode().OwnerDoc()->FullscreenStackTop();
Element* element = AsNode().OwnerDoc()->GetUnretargetedFullScreenElement();
NS_ASSERTION(!element || element->State().HasState(NS_EVENT_STATE_FULLSCREEN),
"Fullscreen element should have fullscreen styles applied");
@ -510,7 +601,7 @@ void DocumentOrShadowRoot::GetAnimations(
child = child->GetNextSibling()) {
if (RefPtr<Element> element = Element::FromNode(child)) {
nsTArray<RefPtr<Animation>> result;
element->GetAnimations(options, result, Element::Flush::No);
element->GetAnimationsWithoutFlush(options, result);
aAnimations.AppendElements(std::move(result));
}
}
@ -660,7 +751,9 @@ nsRadioGroupStruct* DocumentOrShadowRoot::GetOrCreateRadioGroup(
int32_t DocumentOrShadowRoot::StyleOrderIndexOfSheet(
const StyleSheet& aSheet) const {
if (aSheet.IsConstructed()) {
int32_t index = mAdoptedStyleSheets.IndexOf(&aSheet);
// NOTE: constructable sheets can have duplicates, so we need to start
// looking from behind.
int32_t index = mAdoptedStyleSheets.LastIndexOf(&aSheet);
return (index < 0) ? index : index + SheetCount();
}
return mStyleSheets.IndexOf(&aSheet);
@ -677,26 +770,27 @@ void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdoptedStyleSheets)
auto NoteSheets = [tmp, &cb = cb](nsTArray<RefPtr<StyleSheet>>& sheetList) {
for (StyleSheet* sheet : sheetList) {
if (!sheet->IsApplicable()) {
continue;
}
// The style set or mServoStyles keep more references to it if the sheet
// is applicable.
if (tmp->mKind == Kind::ShadowRoot) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
cb.NoteXPCOMChild(sheet);
} else if (tmp->AsNode().AsDocument()->StyleSetFilled()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
cb, "mStyleSet->mRawSet.stylist.stylesheets.author[i]");
cb.NoteXPCOMChild(sheet);
}
auto NoteSheetIfApplicable = [&](StyleSheet& aSheet) {
if (!aSheet.IsApplicable()) {
return;
}
// The style set or mServoStyles keep more references to it if the sheet
// is applicable.
if (tmp->mKind == Kind::ShadowRoot) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
cb.NoteXPCOMChild(&aSheet);
} else if (tmp->AsNode().AsDocument()->StyleSetFilled()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
cb, "mStyleSet->mRawSet.stylist.stylesheets.author[i]");
cb.NoteXPCOMChild(&aSheet);
}
};
NoteSheets(tmp->mStyleSheets);
NoteSheets(tmp->mAdoptedStyleSheets);
for (auto& sheet : tmp->mStyleSheets) {
NoteSheetIfApplicable(*sheet);
}
tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront(NoteSheetIfApplicable);
for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) {
iter.Get()->Traverse(&cb);
@ -719,7 +813,12 @@ void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets);
for (RefPtr<StyleSheet>& sheet : tmp->mAdoptedStyleSheets) {
for (StyleSheet* sheet : tmp->mStyleSheets) {
sheet->ClearAssociatedDocumentOrShadowRoot();
tmp->RemoveSheetFromStylesIfApplicable(*sheet);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheets);
for (StyleSheet* sheet : tmp->mAdoptedStyleSheets) {
sheet->RemoveAdopter(*tmp);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);

View File

@ -86,6 +86,8 @@ class DocumentOrShadowRoot {
void GetAdoptedStyleSheets(nsTArray<RefPtr<StyleSheet>>&) const;
void RemoveStyleSheet(StyleSheet&);
Element* GetElementById(const nsAString& aElementId);
/**
@ -219,16 +221,38 @@ class DocumentOrShadowRoot {
nsIContent* Retarget(nsIContent* aContent) const;
protected:
// Returns the reference to the sheet, if found in mStyleSheets.
already_AddRefed<StyleSheet> RemoveSheet(StyleSheet& aSheet);
void InsertSheetAt(size_t aIndex, StyleSheet& aSheet);
void InsertAdoptedSheetAt(size_t aIndex, StyleSheet& aSheet);
void EnsureAdoptedSheetsAreValid(
void SetAdoptedStyleSheets(
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
ErrorResult& aRv);
// This is needed because ServoStyleSet / ServoAuthorData don't deal with
// duplicate stylesheets (and it's unclear we'd want to support that as it'd
// be a bunch of duplicate work), while adopted stylesheets do need to deal
// with them.
template <typename Callback>
void EnumerateUniqueAdoptedStyleSheetsBackToFront(Callback aCallback) {
StyleSheetSet set(mAdoptedStyleSheets.Length());
for (StyleSheet* sheet : Reversed(mAdoptedStyleSheets)) {
if (MOZ_UNLIKELY(!set.EnsureInserted(sheet))) {
continue;
}
aCallback(*sheet);
}
}
protected:
using StyleSheetSet = nsTHashtable<nsPtrHashKey<const StyleSheet>>;
void RemoveSheetFromStylesIfApplicable(StyleSheet&);
void ClearAdoptedStyleSheets();
/**
* Clone's the argument's adopted style sheets into this.
* This should only be used when cloning a static document for printing.
*/
void CloneAdoptedSheetsFrom(const DocumentOrShadowRoot&);
void InsertSheetAt(size_t aIndex, StyleSheet& aSheet);
void AddSizeOfExcludingThis(nsWindowSizes&) const;
void AddSizeOfOwnedSheetArrayExcludingThis(
nsWindowSizes&, const nsTArray<RefPtr<StyleSheet>>&) const;
@ -243,8 +267,10 @@ class DocumentOrShadowRoot {
nsTArray<RefPtr<StyleSheet>> mStyleSheets;
RefPtr<StyleSheetList> mDOMStyleSheets;
// Style sheets that are adopted by assinging to the `adoptedStyleSheets`
// WebIDL atribute. These can only be constructed stylesheets.
/**
* Style sheets that are adopted by assinging to the `adoptedStyleSheets`
* WebIDL atribute. These can only be constructed stylesheets.
*/
nsTArray<RefPtr<StyleSheet>> mAdoptedStyleSheets;
/*

View File

@ -25,8 +25,9 @@ already_AddRefed<mozilla::dom::DocumentType> NS_NewDOMDocumentType(
nsGkAtoms::documentTypeNodeName, nullptr, kNameSpaceID_None,
nsINode::DOCUMENT_TYPE_NODE, aName);
RefPtr<mozilla::dom::DocumentType> docType = new mozilla::dom::DocumentType(
ni.forget(), aPublicId, aSystemId, aInternalSubset);
RefPtr<mozilla::dom::DocumentType> docType =
new (aNodeInfoManager) mozilla::dom::DocumentType(
ni.forget(), aPublicId, aSystemId, aInternalSubset);
return docType.forget();
}
@ -73,8 +74,8 @@ void DocumentType::GetInternalSubset(nsAString& aInternalSubset) const {
already_AddRefed<CharacterData> DocumentType::CloneDataNode(
mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const {
return do_AddRef(new DocumentType(do_AddRef(aNodeInfo), mPublicId, mSystemId,
mInternalSubset));
return do_AddRef(new (aNodeInfo->NodeInfoManager()) DocumentType(
do_AddRef(aNodeInfo), mPublicId, mSystemId, mInternalSubset));
}
} // namespace dom

View File

@ -24,6 +24,7 @@
#include "mozilla/dom/MutationObservers.h"
#include "mozilla/dom/ScriptLoader.h"
#include "mozilla/dom/Text.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/gfx/Matrix.h"
#include "nsAtom.h"
#include "nsDOMAttributeMap.h"
@ -260,8 +261,7 @@ Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) {
}
EventStates Element::IntrinsicState() const {
return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE
: NS_EVENT_STATE_MOZ_READONLY;
return IsEditable() ? NS_EVENT_STATE_READWRITE : NS_EVENT_STATE_READONLY;
}
void Element::NotifyStateChange(EventStates aStates) {
@ -299,13 +299,26 @@ void Element::UpdateState(bool aNotify) {
} // namespace mozilla
void nsIContent::UpdateEditableState(bool aNotify) {
nsIContent* parent = GetParent();
if (IsInNativeAnonymousSubtree()) {
// Don't propagate the editable flag into native anonymous subtrees.
if (IsRootOfNativeAnonymousSubtree()) {
return;
}
// Don't implicitly set the flag on the root of a native anonymous subtree.
// This needs to be set explicitly, see for example
// nsTextControlFrame::CreateRootNode().
SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE) &&
!IsRootOfNativeAnonymousSubtree());
// We allow setting the flag on NAC (explicitly, see
// nsTextControlFrame::CreateAnonymousContent for example), but not
// unsetting it.
//
// Otherwise, just the act of binding the NAC subtree into our non-anonymous
// parent would clear the flag, which is not good. As we shouldn't move NAC
// around, this is fine.
if (HasFlag(NODE_IS_EDITABLE)) {
return;
}
}
nsIContent* parent = GetParent();
SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
}
namespace mozilla {
@ -321,19 +334,28 @@ void Element::UpdateEditableState(bool aNotify) {
// insertion into the document and UpdateState can be slow for
// some kinds of elements even when not notifying.
if (IsEditable()) {
RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
RemoveStatesSilently(NS_EVENT_STATE_READONLY);
AddStatesSilently(NS_EVENT_STATE_READWRITE);
} else {
RemoveStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
AddStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
RemoveStatesSilently(NS_EVENT_STATE_READWRITE);
AddStatesSilently(NS_EVENT_STATE_READONLY);
}
}
}
int32_t Element::TabIndex() {
const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::tabindex);
Maybe<int32_t> Element::GetTabIndexAttrValue() {
const nsAttrValue* attrVal = GetParsedAttr(nsGkAtoms::tabindex);
if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
return attrVal->GetIntegerValue();
return Some(attrVal->GetIntegerValue());
}
return Nothing();
}
int32_t Element::TabIndex() {
Maybe<int32_t> attrVal = GetTabIndexAttrValue();
if (attrVal.isSome()) {
return attrVal.value();
}
return TabIndexDefault();
@ -341,21 +363,21 @@ int32_t Element::TabIndex() {
void Element::Focus(const FocusOptions& aOptions, ErrorResult& aError) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm) {
return;
}
// Also other browsers seem to have the hack to not re-focus (and flush) when
// the element is already focused.
// Until https://github.com/whatwg/html/issues/4512 is clarified, we'll
// maintain interoperatibility by not re-focusing, independent of aOptions.
// I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll:
// false })` won't re-focus.
if (fm) {
if (fm->CanSkipFocus(this)) {
fm->NeedsFlushBeforeEventHandling(this);
} else {
aError = fm->SetFocus(
this, nsIFocusManager::FLAG_BYELEMENTFOCUS |
nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions));
}
if (fm->CanSkipFocus(this)) {
fm->NeedsFlushBeforeEventHandling(this);
return;
}
aError = fm->SetFocus(
this, nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions));
}
void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) {
@ -493,7 +515,7 @@ void Element::UnlockStyleStates(EventStates aStates) {
locks->mLocks &= ~aStates;
if (locks->mLocks.IsEmpty()) {
DeleteProperty(nsGkAtoms::lockedStyleStates);
RemoveProperty(nsGkAtoms::lockedStyleStates);
ClearHasLockedStyleStates();
delete locks;
} else {
@ -507,7 +529,7 @@ void Element::UnlockStyleStates(EventStates aStates) {
void Element::ClearStyleStateLocks() {
StyleStateLocks locks = LockedStyleStates();
DeleteProperty(nsGkAtoms::lockedStyleStates);
RemoveProperty(nsGkAtoms::lockedStyleStates);
ClearHasLockedStyleStates();
NotifyStyleStateChange(locks.mLocks);
@ -967,7 +989,7 @@ nsRect Element::GetClientAreaRect() {
// document, we have overlay scrollbars, and we aren't embedded in another
// document
bool overlayScrollbars =
LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0;
LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0;
bool rootContentDocument =
presContext && presContext->IsRootContentDocument();
if (overlayScrollbars && rootContentDocument &&
@ -1218,8 +1240,9 @@ already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
* context object's node document, host is context object,
* and mode is init's mode.
*/
auto* nim = nodeInfo->NodeInfoManager();
RefPtr<ShadowRoot> shadowRoot =
new ShadowRoot(this, aMode, nodeInfo.forget());
new (nim) ShadowRoot(this, aMode, nodeInfo.forget());
if (NodeOrAncestorHasDirAuto()) {
shadowRoot->SetAncestorHasDirAuto();
@ -1726,7 +1749,6 @@ nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) {
// Now recurse into our kids. Ensure this happens after binding the shadow
// root so that directionality of slots is updated.
{
BindContext::NestingLevel level(aContext, *this);
for (nsIContent* child = GetFirstChild(); child;
child = child->GetNextSibling()) {
rv = child->BindToTree(aContext, *this);
@ -1911,14 +1933,14 @@ void Element::UnbindFromTree(bool aNullParent) {
//
// FIXME (Bug 522599): Need a test for this.
if (MayHaveAnimations()) {
DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty);
DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
DeleteProperty(nsGkAtoms::transitionsOfMarkerProperty);
DeleteProperty(nsGkAtoms::transitionsProperty);
DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
DeleteProperty(nsGkAtoms::animationsOfMarkerProperty);
DeleteProperty(nsGkAtoms::animationsProperty);
RemoveProperty(nsGkAtoms::transitionsOfBeforeProperty);
RemoveProperty(nsGkAtoms::transitionsOfAfterProperty);
RemoveProperty(nsGkAtoms::transitionsOfMarkerProperty);
RemoveProperty(nsGkAtoms::transitionsProperty);
RemoveProperty(nsGkAtoms::animationsOfBeforeProperty);
RemoveProperty(nsGkAtoms::animationsOfAfterProperty);
RemoveProperty(nsGkAtoms::animationsOfMarkerProperty);
RemoveProperty(nsGkAtoms::animationsProperty);
if (document) {
if (nsPresContext* presContext = document->GetPresContext()) {
// We have to clear all pending restyle requests for the animations on
@ -2050,9 +2072,7 @@ void Element::SetSMILOverrideStyleDeclaration(DeclarationBlock& aDeclaration) {
bool Element::IsLabelable() const { return false; }
bool Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const {
return false;
}
bool Element::IsInteractiveHTMLContent() const { return false; }
DeclarationBlock* Element::GetInlineStyleDeclaration() const {
if (!MayHaveStyle()) {
@ -2613,8 +2633,7 @@ bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
return true;
}
if (aAttribute == nsGkAtoms::exportparts &&
StaticPrefs::layout_css_shadow_parts_enabled()) {
if (aAttribute == nsGkAtoms::exportparts) {
aResult.ParsePartMapping(aValue);
return true;
}
@ -3325,8 +3344,78 @@ CORSMode Element::AttrValueToCORSMode(const nsAttrValue* aValue) {
return CORSMode(aValue->GetEnumValue());
}
/**
* Returns nullptr if requests for fullscreen are allowed in the current
* context. Requests are only allowed if the user initiated them (like with
* a mouse-click or key press), unless this check has been disabled by
* setting the pref "full-screen-api.allow-trusted-requests-only" to false
* or if the caller is privileged. Feature policy may also deny requests.
* If fullscreen is not allowed, a key for the error message is returned.
*/
static const char* GetFullscreenError(CallerType aCallerType) {
return nsContentUtils::CheckRequestFullscreenAllowed(aCallerType);
// Privileged callers can always request fullscreen
if (aCallerType == CallerType::System) {
return nullptr;
}
// Ensure feature policy allows using the fullscreen API
/*if (!FeaturePolicyUtils::IsFeatureAllowed(aDocument,
NS_LITERAL_STRING("fullscreen"))) {
return "FullscreenDeniedFeaturePolicy";
}*/
// Bypass user interaction checks if preference is set
if (!StaticPrefs::full_screen_api_allow_trusted_requests_only()) {
return nullptr;
}
if (!UserActivation::IsHandlingUserInput()) {
return "FullscreenDeniedNotInputDriven";
}
// If more time has elapsed since the user input than is specified by the
// dom.event.handling-user-input-time-limit pref (default 1 second),
// disallow fullscreen
TimeDuration timeout = nsContentUtils::HandlingUserInputTimeout();
if (timeout > TimeDuration(nullptr) &&
(TimeStamp::Now() - UserActivation::GetHandlingInputStart()) > timeout) {
return "FullscreenDeniedNotInputDriven";
}
// Entering full-screen on mouse mouse event is only allowed with left mouse
// button
if (StaticPrefs::full_screen_api_mouse_event_allow_left_button_only() &&
(EventStateManager::sCurrentMouseBtn == MouseButton::eMiddle ||
EventStateManager::sCurrentMouseBtn == MouseButton::eRight)) {
return "FullscreenDeniedMouseEventOnlyLeftBtn";
}
return nullptr;
}
void Element::SetCapture(bool aRetargetToElement) {
// If there is already an active capture, ignore this request. This would
// occur if a splitter, frame resizer, etc had already captured and we don't
// want to override those.
if (!PresShell::GetCapturingContent()) {
PresShell::SetCapturingContent(
this, CaptureFlags::PreventDragStart |
(aRetargetToElement ? CaptureFlags::RetargetToElement
: CaptureFlags::None));
}
}
void Element::SetCaptureAlways(bool aRetargetToElement) {
PresShell::SetCapturingContent(
this, CaptureFlags::PreventDragStart | CaptureFlags::IgnoreAllowedState |
(aRetargetToElement ? CaptureFlags::RetargetToElement
: CaptureFlags::None));
}
void Element::ReleaseCapture() {
if (PresShell::GetCapturingContent() == this) {
PresShell::ReleaseCapturingContent();
}
}
already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
@ -3334,12 +3423,6 @@ already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
auto request = FullscreenRequest::Create(this, aCallerType, aRv);
RefPtr<Promise> promise = request->GetPromise();
if (!FeaturePolicyUtils::IsFeatureAllowed(OwnerDoc(),
NS_LITERAL_STRING("fullscreen"))) {
request->Reject("FullscreenDeniedFeaturePolicy");
return promise.forget();
}
// Only grant fullscreen requests if this is called from inside a trusted
// event handler (i.e. inside an event handler for a user initiated event).
// This stops the fullscreen from being abused similar to the popups of old,
@ -3360,12 +3443,11 @@ void Element::RequestPointerLock(CallerType aCallerType) {
}
already_AddRefed<Flex> Element::GetAsFlexContainer() {
nsIFrame* frame = GetPrimaryFrame();
// We need the flex frame to compute additional info, and use
// that annotated version of the frame.
nsFlexContainerFrame* flexFrame =
nsFlexContainerFrame::GetFlexFrameWithComputedInfo(frame);
nsFlexContainerFrame::GetFlexFrameWithComputedInfo(
GetPrimaryFrame(FlushType::Layout));
if (flexFrame) {
RefPtr<Flex> flex = new Flex(this, flexFrame);
@ -3376,7 +3458,8 @@ already_AddRefed<Flex> Element::GetAsFlexContainer() {
void Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult) {
nsGridContainerFrame* frame =
nsGridContainerFrame::GetGridFrameWithComputedInfo(GetPrimaryFrame());
nsGridContainerFrame::GetGridFrameWithComputedInfo(
GetPrimaryFrame(FlushType::Layout));
// If we get a nsGridContainerFrame from the prior call,
// all the next-in-flow frames will also be nsGridContainerFrames.
@ -3442,29 +3525,7 @@ already_AddRefed<Animation> Element::Animate(
JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError) {
Nullable<ElementOrCSSPseudoElement> target;
target.SetValue().SetAsElement() = this;
return Animate(target, aContext, aKeyframes, aOptions, aError);
}
/* static */
already_AddRefed<Animation> Element::Animate(
const Nullable<ElementOrCSSPseudoElement>& aTarget, JSContext* aContext,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError) {
MOZ_ASSERT(!aTarget.IsNull() && (aTarget.Value().IsElement() ||
aTarget.Value().IsCSSPseudoElement()),
"aTarget should be initialized");
RefPtr<Element> referenceElement;
if (aTarget.Value().IsElement()) {
referenceElement = &aTarget.Value().GetAsElement();
} else {
referenceElement = aTarget.Value().GetAsCSSPseudoElement().Element();
}
nsCOMPtr<nsIGlobalObject> ownerGlobal = referenceElement->GetOwnerGlobal();
nsCOMPtr<nsIGlobalObject> ownerGlobal = GetOwnerGlobal();
if (!ownerGlobal) {
aError.Throw(NS_ERROR_FAILURE);
return nullptr;
@ -3476,8 +3537,8 @@ already_AddRefed<Animation> Element::Animate(
// convention and needs to be called in caller's compartment.
// This should match to RunConstructorInCallerCompartment attribute in
// KeyframeEffect.webidl.
RefPtr<KeyframeEffect> effect = KeyframeEffect::Constructor(
global, aTarget, aKeyframes, aOptions, aError);
RefPtr<KeyframeEffect> effect =
KeyframeEffect::Constructor(global, this, aKeyframes, aOptions, aError);
if (aError.Failed()) {
return nullptr;
}
@ -3486,7 +3547,7 @@ already_AddRefed<Animation> Element::Animate(
// needs to be called in the target element's realm.
JSAutoRealm ar(aContext, global.Get());
AnimationTimeline* timeline = referenceElement->OwnerDoc()->Timeline();
AnimationTimeline* timeline = OwnerDoc()->Timeline();
RefPtr<Animation> animation = Animation::Constructor(
global, effect, Optional<AnimationTimeline*>(timeline), aError);
if (aError.Failed()) {
@ -3506,23 +3567,26 @@ already_AddRefed<Animation> Element::Animate(
}
void Element::GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations,
Flush aFlush) {
if (aFlush == Flush::Yes) {
if (Document* doc = GetComposedDoc()) {
// We don't need to explicitly flush throttled animations here, since
// updating the animation style of elements will never affect the set of
// running animations and it's only the set of running animations that is
// important here.
//
// NOTE: Any changes to the flags passed to the following call should
// be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations
// too.
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
}
nsTArray<RefPtr<Animation>>& aAnimations) {
if (Document* doc = GetComposedDoc()) {
// We don't need to explicitly flush throttled animations here, since
// updating the animation style of elements will never affect the set of
// running animations and it's only the set of running animations that is
// important here.
//
// NOTE: Any changes to the flags passed to the following call should
// be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations
// too.
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
}
GetAnimationsWithoutFlush(aOptions, aAnimations);
}
void Element::GetAnimationsWithoutFlush(
const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations) {
Element* elem = this;
PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
// For animations on generated-content elements, the animations are stored
@ -3632,8 +3696,8 @@ void Element::SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError) {
localName = nsGkAtoms::body;
namespaceID = kNameSpaceID_XHTML;
}
RefPtr<DocumentFragment> fragment =
new DocumentFragment(OwnerDoc()->NodeInfoManager());
RefPtr<DocumentFragment> fragment = new (OwnerDoc()->NodeInfoManager())
DocumentFragment(OwnerDoc()->NodeInfoManager());
nsContentUtils::ParseFragmentHTML(
aOuterHTML, fragment, localName, namespaceID,
OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
@ -3869,15 +3933,32 @@ float Element::FontSizeInflation() {
return 1.0;
}
ReferrerPolicy Element::GetReferrerPolicyAsEnum() {
void Element::GetImplementedPseudoElement(nsAString& aPseudo) const {
PseudoStyleType pseudoType = GetPseudoElementType();
if (pseudoType == PseudoStyleType::NotPseudo) {
return SetDOMStringToNull(aPseudo);
}
nsDependentAtomString pseudo(nsCSSPseudoElements::GetPseudoAtom(pseudoType));
// We want to use the modern syntax (::placeholder, etc), but the atoms only
// contain one semi-colon.
MOZ_ASSERT(pseudo.Length() > 2 && pseudo[0] == ':' && pseudo[1] != ':');
aPseudo.Truncate();
aPseudo.SetCapacity(pseudo.Length() + 1);
aPseudo.Append(':');
aPseudo.Append(pseudo);
}
ReferrerPolicy Element::GetReferrerPolicyAsEnum() const {
if (IsHTMLElement()) {
const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrerpolicy);
return ReferrerPolicyFromAttr(referrerValue);
return ReferrerPolicyFromAttr(GetParsedAttr(nsGkAtoms::referrerpolicy));
}
return ReferrerPolicy::_empty;
}
ReferrerPolicy Element::ReferrerPolicyFromAttr(const nsAttrValue* aValue) {
ReferrerPolicy Element::ReferrerPolicyFromAttr(
const nsAttrValue* aValue) const {
if (aValue && aValue->Type() == nsAttrValue::eEnum) {
return ReferrerPolicy(aValue->GetEnumValue());
}
@ -3952,14 +4033,14 @@ void Element::UnregisterIntersectionObserver(
if (observers) {
observers->Remove(aObserver);
if (observers->IsEmpty()) {
DeleteProperty(nsGkAtoms::intersectionobserverlist);
RemoveProperty(nsGkAtoms::intersectionobserverlist);
}
}
}
void Element::UnlinkIntersectionObservers() {
// IntersectionObserverPropertyDtor takes care of the hard work.
DeleteProperty(nsGkAtoms::intersectionobserverlist);
RemoveProperty(nsGkAtoms::intersectionobserverlist);
}
bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver,

View File

@ -17,16 +17,15 @@
#include "nsChangeHint.h"
#include "nsContentUtils.h"
#include "nsDOMAttributeMap.h"
#include "nsStyleConsts.h"
#include "nsINodeList.h"
#include "nsIScrollableFrame.h"
#include "nsPresContext.h"
#include "Units.h"
#include "mozilla/Attributes.h"
#include "mozilla/CORSMode.h"
#include "mozilla/EventForwards.h"
#include "mozilla/EventStates.h"
#include "mozilla/FlushType.h"
#include "mozilla/PresShell.h"
#include "mozilla/PseudoStyleType.h"
#include "mozilla/RustCell.h"
#include "mozilla/SMILAttr.h"
@ -162,7 +161,7 @@ class Element : public FragmentOrElement {
#ifdef MOZILLA_INTERNAL_API
explicit Element(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
: FragmentOrElement(std::move(aNodeInfo)),
mState(NS_EVENT_STATE_MOZ_READONLY | NS_EVENT_STATE_DEFINED) {
mState(NS_EVENT_STATE_READONLY | NS_EVENT_STATE_DEFINED) {
MOZ_ASSERT(mNodeInfo->NodeType() == ELEMENT_NODE,
"Bad NodeType in aNodeInfo");
SetIsElement();
@ -216,6 +215,11 @@ class Element : public FragmentOrElement {
*/
int32_t TabIndex();
/**
* Get the parsed value of tabindex attribute.
*/
Maybe<int32_t> GetTabIndexAttrValue();
/**
* Set tabIndex value to this element.
*/
@ -378,7 +382,7 @@ class Element : public FragmentOrElement {
/**
* Returns if the element is interactive content as per HTML specification.
*/
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const;
virtual bool IsInteractiveHTMLContent() const;
/**
* Returns |this| as an nsIMozBrowserFrame* if the element is a frame or
@ -1180,31 +1184,11 @@ class Element : public FragmentOrElement {
}
return false;
}
void SetCapture(bool aRetargetToElement) {
// If there is already an active capture, ignore this request. This would
// occur if a splitter, frame resizer, etc had already captured and we don't
// want to override those.
if (!PresShell::GetCapturingContent()) {
PresShell::SetCapturingContent(
this, CaptureFlags::PreventDragStart |
(aRetargetToElement ? CaptureFlags::RetargetToElement
: CaptureFlags::None));
}
}
void SetCapture(bool aRetargetToElement);
void SetCaptureAlways(bool aRetargetToElement) {
PresShell::SetCapturingContent(
this, CaptureFlags::PreventDragStart |
CaptureFlags::IgnoreAllowedState |
(aRetargetToElement ? CaptureFlags::RetargetToElement
: CaptureFlags::None));
}
void SetCaptureAlways(bool aRetargetToElement);
void ReleaseCapture() {
if (PresShell::GetCapturingContent() == this) {
PresShell::ReleaseCapturingContent();
}
}
void ReleaseCapture();
already_AddRefed<Promise> RequestFullscreen(CallerType, ErrorResult&);
void RequestPointerLock(CallerType aCallerType);
@ -1275,48 +1259,52 @@ class Element : public FragmentOrElement {
MOZ_CAN_RUN_SCRIPT int32_t ScrollHeight();
MOZ_CAN_RUN_SCRIPT void MozScrollSnap();
MOZ_CAN_RUN_SCRIPT int32_t ClientTop() {
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().y);
return CSSPixel::FromAppUnits(GetClientAreaRect().y).Rounded();
}
MOZ_CAN_RUN_SCRIPT int32_t ClientLeft() {
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().x);
return CSSPixel::FromAppUnits(GetClientAreaRect().x).Rounded();
}
MOZ_CAN_RUN_SCRIPT int32_t ClientWidth() {
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().Width());
return CSSPixel::FromAppUnits(GetClientAreaRect().Width()).Rounded();
}
MOZ_CAN_RUN_SCRIPT int32_t ClientHeight() {
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().Height());
return CSSPixel::FromAppUnits(GetClientAreaRect().Height()).Rounded();
}
MOZ_CAN_RUN_SCRIPT int32_t ScrollTopMin() {
nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().y)
: 0;
if (!sf) {
return 0;
}
return CSSPixel::FromAppUnits(sf->GetScrollRange().y).Rounded();
}
MOZ_CAN_RUN_SCRIPT int32_t ScrollTopMax() {
nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels(
sf->GetScrollRange().YMost())
: 0;
if (!sf) {
return 0;
}
return CSSPixel::FromAppUnits(sf->GetScrollRange().YMost()).Rounded();
}
MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMin() {
nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().x)
: 0;
if (!sf) {
return 0;
}
return CSSPixel::FromAppUnits(sf->GetScrollRange().x).Rounded();
}
MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMax() {
nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels(
sf->GetScrollRange().XMost())
: 0;
if (!sf) {
return 0;
}
return CSSPixel::FromAppUnits(sf->GetScrollRange().XMost()).Rounded();
}
MOZ_CAN_RUN_SCRIPT double ClientHeightDouble() {
return nsPresContext::AppUnitsToDoubleCSSPixels(
GetClientAreaRect().Height());
return CSSPixel::FromAppUnits(GetClientAreaRect().Height());
}
MOZ_CAN_RUN_SCRIPT double ClientWidthDouble() {
return nsPresContext::AppUnitsToDoubleCSSPixels(
GetClientAreaRect().Width());
return CSSPixel::FromAppUnits(GetClientAreaRect().Width());
}
// This function will return the block size of first line box, no matter if
@ -1337,20 +1325,12 @@ class Element : public FragmentOrElement {
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError);
// A helper method that factors out the common functionality needed by
// Element::Animate and CSSPseudoElement::Animate
static already_AddRefed<Animation> Animate(
const Nullable<ElementOrCSSPseudoElement>& aTarget, JSContext* aContext,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError);
enum class Flush { Yes, No };
MOZ_CAN_RUN_SCRIPT
void GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations,
Flush aFlush = Flush::Yes);
nsTArray<RefPtr<Animation>>& aAnimations);
void GetAnimationsWithoutFlush(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations);
static void GetAnimationsUnsorted(Element* aElement,
PseudoStyleType aPseudoType,
@ -1588,8 +1568,10 @@ class Element : public FragmentOrElement {
*/
float FontSizeInflation();
ReferrerPolicy GetReferrerPolicyAsEnum();
ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue);
void GetImplementedPseudoElement(nsAString&) const;
ReferrerPolicy GetReferrerPolicyAsEnum() const;
ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue) const;
/*
* Helpers for .dataset. This is implemented on Element, though only some
@ -2039,6 +2021,11 @@ inline mozilla::dom::Element* nsINode::GetPreviousElementSibling() const {
return nullptr;
}
inline mozilla::dom::Element* nsINode::GetAsElementOrParentElement() const {
return IsElement() ? const_cast<mozilla::dom::Element*>(AsElement())
: GetParentElement();
}
inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
nsIContent* nextSibling = GetNextSibling();
while (nextSibling) {
@ -2060,7 +2047,8 @@ inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
nsINode** aResult) const { \
*aResult = nullptr; \
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo); \
RefPtr<_elementName> it = new _elementName(ni.forget()); \
auto* nim = ni->NodeInfoManager(); \
RefPtr<_elementName> it = new (nim) _elementName(ni.forget()); \
nsresult rv = const_cast<_elementName*>(this)->CopyInnerTo(it); \
if (NS_SUCCEEDED(rv)) { \
it.forget(aResult); \
@ -2075,8 +2063,9 @@ inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
nsINode** aResult) const { \
*aResult = nullptr; \
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo); \
auto* nim = ni->NodeInfoManager(); \
RefPtr<_elementName> it = \
new _elementName(ni.forget() EXPAND extra_args_); \
new (nim) _elementName(ni.forget() EXPAND extra_args_); \
nsresult rv = it->Init(); \
nsresult rv2 = const_cast<_elementName*>(this)->CopyInnerTo(it); \
if (NS_FAILED(rv2)) { \

View File

@ -929,10 +929,9 @@ void EventSourceImpl::SetupHttpChannel() {
nsresult EventSourceImpl::SetupReferrerInfo() {
AssertIsOnMainThread();
MOZ_ASSERT(!IsShutDown());
nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent();
if (doc) {
nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
referrerInfo->InitWithDocument(doc);
if (nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent()) {
auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
nsresult rv = mHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1781,7 +1780,7 @@ EventSource::EventSource(nsIGlobalObject* aGlobal,
mImpl = new EventSourceImpl(this, aCookieSettings);
}
EventSource::~EventSource() {}
EventSource::~EventSource() = default;
nsresult EventSource::CreateAndDispatchSimpleEvent(const nsAString& aName) {
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);

View File

@ -0,0 +1,57 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Implementation of a generic filtered iterator over nodes.
*/
#ifndef mozilla_dom_FilteredNodeIterator_h
#define mozilla_dom_FilteredNodeIterator_h
#include "nsINode.h"
namespace mozilla {
namespace dom {
template <typename T, typename Iter>
class FilteredNodeIterator : public Iter {
public:
explicit FilteredNodeIterator(const nsINode& aNode) : Iter(aNode) {
EnsureValid();
}
FilteredNodeIterator& begin() { return *this; }
using Iter::end;
void operator++() {
Iter::operator++();
EnsureValid();
}
using Iter::operator!=;
T* operator*() {
nsINode* node = Iter::operator*();
MOZ_ASSERT(!node || T::FromNode(node));
return static_cast<T*>(node);
}
private:
void EnsureValid() {
while (true) {
nsINode* node = Iter::operator*();
if (!node || T::FromNode(node)) {
return;
}
Iter::operator++();
}
}
FilteredNodeIterator() : Iter() {}
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FilteredNodeIterator.h

View File

@ -15,14 +15,13 @@ using namespace mozilla;
using namespace mozilla::dom;
FormData::FormData(nsISupports* aOwner, NotNull<const Encoding*> aEncoding,
Element* aOriginatingElement)
: HTMLFormSubmission(nullptr, EmptyString(), aEncoding,
aOriginatingElement),
Element* aSubmitter)
: HTMLFormSubmission(nullptr, EmptyString(), aEncoding, aSubmitter),
mOwner(aOwner) {}
FormData::FormData(const FormData& aFormData)
: HTMLFormSubmission(aFormData.mActionURL, aFormData.mTarget,
aFormData.mEncoding, aFormData.mOriginatingElement) {
aFormData.mEncoding, aFormData.mSubmitter) {
mOwner = aFormData.mOwner;
mFormData = aFormData.mFormData;
}

View File

@ -25,7 +25,7 @@ class FormData final : public nsISupports,
public nsWrapperCache {
private:
FormData(const FormData& aFormData);
~FormData() {}
~FormData() = default;
struct FormDataTuple {
nsString name;
@ -50,7 +50,7 @@ class FormData final : public nsISupports,
public:
explicit FormData(nsISupports* aOwner = nullptr,
NotNull<const Encoding*> aEncoding = UTF_8_ENCODING,
Element* aOriginatingElement = nullptr);
Element* aSubmitter = nullptr);
already_AddRefed<FormData> Clone();

View File

@ -146,8 +146,11 @@ NS_INTERFACE_MAP_BEGIN(nsIContent)
NS_INTERFACE_MAP_END
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsIContent)
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
nsIContent, LastRelease())
NS_IMPL_DOMARENA_DESTROY(nsIContent)
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(
nsIContent, LastRelease(), Destroy())
nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
// This handles also nested native anonymous content.
@ -196,7 +199,7 @@ nsIContent::IMEState nsIContent::GetDesiredIMEState() {
// Check for the special case where we're dealing with elements which don't
// have the editable flag set, but are readwrite (such as text controls).
if (!IsElement() ||
!AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
!AsElement()->State().HasState(NS_EVENT_STATE_READWRITE)) {
return IMEState(IMEState::DISABLED);
}
}
@ -226,7 +229,7 @@ nsIContent::IMEState nsIContent::GetDesiredIMEState() {
return state;
}
bool nsIContent::HasIndependentSelection() {
bool nsIContent::HasIndependentSelection() const {
nsIFrame* frame = GetPrimaryFrame();
return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
}
@ -279,22 +282,18 @@ nsresult nsIContent::LookupNamespaceURIInternal(
}
// Trace up the content parent chain looking for the namespace
// declaration that declares aNamespacePrefix.
const nsIContent* content = this;
do {
if (content->IsElement() &&
content->AsElement()->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
for (Element* element = GetAsElementOrParentElement(); element;
element = element->GetParentElement()) {
if (element->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) {
return NS_OK;
} while ((content = content->GetParent()));
}
}
return NS_ERROR_FAILURE;
}
nsAtom* nsIContent::GetLang() const {
for (const auto* content = this; content; content = content->GetParent()) {
if (!content->IsElement()) {
continue;
}
auto* element = content->AsElement();
for (const Element* element = GetAsElementOrParentElement(); element;
element = element->GetParentElement()) {
if (!element->GetAttrCount()) {
continue;
}
@ -1325,14 +1324,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
nsStaticAtom* const* props =
Element::HTMLSVGPropertiesToTraverseAndUnlink();
for (uint32_t i = 0; props[i]; ++i) {
tmp->DeleteProperty(props[i]);
tmp->RemoveProperty(props[i]);
}
}
if (tmp->MayHaveAnimations()) {
nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
for (uint32_t i = 0; effectProps[i]; ++i) {
tmp->DeleteProperty(effectProps[i]);
tmp->RemoveProperty(effectProps[i]);
}
}
}
@ -1794,12 +1793,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
orphan.AppendLiteral(" (orphan)");
}
static const char* kNSURIs[] = {" ([none])", " (xmlns)", " (xml)",
" (xhtml)", " (XLink)", " (XSLT)",
" (XBL)", " (MathML)", " (RDF)",
" (XUL)", " (SVG)", " (XML Events)"};
const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
SprintfLiteral(name, "FragmentOrElement%s %s%s%s%s %s", nsuri,
const char* nsuri = nsNameSpaceManager::GetNameSpaceDisplayName(nsid);
SprintfLiteral(name, "FragmentOrElement %s %s%s%s%s %s", nsuri,
localName.get(), NS_ConvertUTF16toUTF8(id).get(),
NS_ConvertUTF16toUTF8(classes).get(), orphan.get(),
uri.get());
@ -2048,16 +2043,17 @@ void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
nsAutoScriptLoaderDisabler sld(doc);
nsAtom* contextLocalName = NodeInfo()->NameAtom();
int32_t contextNameSpaceID = GetNameSpaceID();
FragmentOrElement* parseContext = this;
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
// Fix up the context to be the host of the ShadowRoot.
contextLocalName = shadowRoot->GetHost()->NodeInfo()->NameAtom();
contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
// Fix up the context to be the host of the ShadowRoot. See
// https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1.
parseContext = shadowRoot->GetHost();
}
if (doc->IsHTMLDocument()) {
nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom();
int32_t contextNameSpaceID = parseContext->GetNameSpaceID();
int32_t oldChildCount = target->GetChildCount();
aError = nsContentUtils::ParseFragmentHTML(
aInnerHTML, target, contextLocalName, contextNameSpaceID,
@ -2068,14 +2064,14 @@ void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
oldChildCount);
} else {
RefPtr<DocumentFragment> df = nsContentUtils::CreateContextualFragment(
target, aInnerHTML, true, aError);
parseContext, aInnerHTML, true, aError);
if (!aError.Failed()) {
// Suppress assertion about node removal mutation events that can't have
// listeners anyway, because no one has had the chance to register
// mutation listeners on the fragment that comes from the parser.
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
static_cast<nsINode*>(target)->AppendChild(*df, aError);
target->AppendChild(*df, aError);
mb.NodesAdded();
}
}

View File

@ -55,7 +55,7 @@ class nsNodeSupportsWeakRefTearoff final : public nsISupportsWeakReference {
NS_DECL_CYCLE_COLLECTION_CLASS(nsNodeSupportsWeakRefTearoff)
private:
~nsNodeSupportsWeakRefTearoff() {}
~nsNodeSupportsWeakRefTearoff() = default;
nsCOMPtr<nsINode> mNode;
};

View File

@ -39,12 +39,16 @@ class FullscreenChange : public LinkedListElement<FullscreenChange> {
}
}
void MayRejectPromise() const {
void MayRejectPromise(const nsACString& aMessage) {
if (mPromise) {
MOZ_ASSERT(mPromise->State() == Promise::PromiseState::Pending);
mPromise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
mPromise->MaybeRejectWithTypeError(aMessage);
}
}
template <int N>
void MayRejectPromise(const char (&aMessage)[N]) {
MayRejectPromise(nsLiteralCString(aMessage));
}
protected:
typedef dom::Promise Promise;
@ -89,14 +93,14 @@ class FullscreenRequest : public FullscreenChange {
// Reject the fullscreen request with the given reason.
// It will dispatch the fullscreenerror event.
void Reject(const char* aReason) const {
void Reject(const char* aReason) {
if (nsPresContext* presContext = Document()->GetPresContext()) {
auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
FullscreenEventType::Error, Document(), mElement);
presContext->RefreshDriver()->ScheduleFullscreenEvent(
std::move(pendingEvent));
}
MayRejectPromise();
MayRejectPromise("Fullscreen request denied");
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("DOM"), Document(),
nsContentUtils::eDOM_PROPERTIES, aReason);
@ -143,7 +147,7 @@ class FullscreenExit : public FullscreenChange {
return WrapUnique(new FullscreenExit(aDoc, nullptr));
}
~FullscreenExit() { MOZ_COUNT_DTOR(FullscreenExit); }
MOZ_COUNTED_DTOR(FullscreenExit)
private:
FullscreenExit(dom::Document* aDoc, already_AddRefed<Promise> aPromise)

Some files were not shown because too many files have changed in this diff Show More