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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,10 +4,10 @@
#include "AnimationUtils.h" #include "AnimationUtils.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "mozilla/dom/KeyframeEffect.h" #include "mozilla/dom/KeyframeEffect.h"
#include "mozilla/EffectSet.h" #include "mozilla/EffectSet.h"
#include "mozilla/Preferences.h"
#include "nsDebug.h" #include "nsDebug.h"
#include "nsAtom.h" #include "nsAtom.h"
#include "nsIContent.h" #include "nsIContent.h"
@ -56,20 +56,6 @@ Document* AnimationUtils::GetDocumentFromGlobal(JSObject* aGlobalObject) {
return win->GetDoc(); 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 */ /* static */
bool AnimationUtils::FrameHasAnimatedScale(const nsIFrame* aFrame) { bool AnimationUtils::FrameHasAnimatedScale(const nsIFrame* aFrame) {
EffectSet* effectSet = EffectSet::GetEffectSetForFrame( EffectSet* effectSet = EffectSet::GetEffectSetForFrame(

View File

@ -71,11 +71,6 @@ class AnimationUtils {
*/ */
static Document* GetDocumentFromGlobal(JSObject* aGlobalObject); 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. * 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() { CSSPseudoElement::~CSSPseudoElement() {
// Element might have been unlinked already, so we have to do null check. // Element might have been unlinked already, so we have to do null check.
if (mOriginatingElement) { if (mOriginatingElement) {
mOriginatingElement->DeleteProperty( mOriginatingElement->RemoveProperty(
GetCSSPseudoElementPropertyAtom(mPseudoType)); GetCSSPseudoElementPropertyAtom(mPseudoType));
} }
} }
@ -43,31 +43,6 @@ JSObject* CSSPseudoElement::WrapObject(JSContext* aCx,
return CSSPseudoElement_Binding::Wrap(aCx, this, aGivenProto); 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 */ /* static */
already_AddRefed<CSSPseudoElement> CSSPseudoElement::GetCSSPseudoElement( already_AddRefed<CSSPseudoElement> CSSPseudoElement::GetCSSPseudoElement(
dom::Element* aElement, PseudoStyleType aType) { dom::Element* aElement, PseudoStyleType aType) {

View File

@ -52,13 +52,6 @@ class CSSPseudoElement final : public nsWrapperCache {
return retVal.forget(); 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 // Given an element:pseudoType pair, returns the CSSPseudoElement stored as a
// property on |aElement|. If there is no CSSPseudoElement for the specified // property on |aElement|. If there is no CSSPseudoElement for the specified
// pseudo-type on element, a new CSSPseudoElement will be created and stored // 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 "DocumentTimeline.h"
#include "mozilla/ScopeExit.h" #include "mozilla/ScopeExit.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/DocumentTimelineBinding.h" #include "mozilla/dom/DocumentTimelineBinding.h"
#include "AnimationUtils.h" #include "AnimationUtils.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
@ -58,8 +59,7 @@ already_AddRefed<DocumentTimeline> DocumentTimeline::Constructor(
if (originTime == TimeDuration::Forever() || if (originTime == TimeDuration::Forever() ||
originTime == -TimeDuration::Forever()) { originTime == -TimeDuration::Forever()) {
aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>( aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>("Origin time");
NS_LITERAL_STRING("Origin time"));
return nullptr; return nullptr;
} }
RefPtr<DocumentTimeline> timeline = new DocumentTimeline(doc, originTime); RefPtr<DocumentTimeline> timeline = new DocumentTimeline(doc, originTime);

View File

@ -269,6 +269,12 @@ void EffectCompositor::PostRestyleForAnimation(dom::Element* aElement,
return; 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); dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
if (!element) { if (!element) {
return; return;
@ -389,19 +395,31 @@ static void ComposeSortedEffects(
const nsTArray<KeyframeEffect*>& aSortedEffects, const nsTArray<KeyframeEffect*>& aSortedEffects,
const EffectSet* aEffectSet, EffectCompositor::CascadeLevel aCascadeLevel, const EffectSet* aEffectSet, EffectCompositor::CascadeLevel aCascadeLevel,
RawServoAnimationValueMap* aAnimationValues) { RawServoAnimationValueMap* aAnimationValues) {
// If multiple animations affect the same property, animations with higher const bool isTransition =
// composite order (priority) override or add to animations with lower aCascadeLevel == EffectCompositor::CascadeLevel::Transitions;
// priority.
nsCSSPropertyIDSet propertiesToSkip; 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) { if (aEffectSet) {
propertiesToSkip = propertiesToSkip =
aCascadeLevel == EffectCompositor::CascadeLevel::Animations isTransition ? aEffectSet->PropertiesForAnimationsLevel()
? aEffectSet->PropertiesForAnimationsLevel().Inverse() : aEffectSet->PropertiesForAnimationsLevel().Inverse();
: aEffectSet->PropertiesForAnimationsLevel();
} }
for (KeyframeEffect* effect : aSortedEffects) { 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; return false;
} }
const bool isTransition = aCascadeLevel == CascadeLevel::Transitions;
// Get a list of effects sorted by composite order. // Get a list of effects sorted by composite order.
nsTArray<KeyframeEffect*> sortedEffectList(effectSet->Count()); nsTArray<KeyframeEffect*> sortedEffectList(effectSet->Count());
for (KeyframeEffect* effect : *effectSet) { 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); sortedEffectList.AppendElement(effect);
} }
if (sortedEffectList.IsEmpty()) {
return false;
}
sortedEffectList.Sort(EffectCompositeOrderComparator()); sortedEffectList.Sort(EffectCompositeOrderComparator());
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel, ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
@ -444,14 +478,14 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(), MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(),
"Should not be in print preview"); "Should not be in print preview");
Maybe<NonOwningAnimationTarget> target = aEffect.GetTarget(); NonOwningAnimationTarget target = aEffect.GetAnimationTarget();
if (!target) { if (!target) {
return false; return false;
} }
// Don't try to compose animations for elements in documents without a pres // Don't try to compose animations for elements in documents without a pres
// shell (e.g. XMLHttpRequest documents). // shell (e.g. XMLHttpRequest documents).
if (!nsContentUtils::GetPresShellForContent(target->mElement)) { if (!nsContentUtils::GetPresShellForContent(target.mElement)) {
return false; return false;
} }
@ -459,11 +493,10 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
// where the cascade results are updated in the pre-traversal as needed. // where the cascade results are updated in the pre-traversal as needed.
// This function, however, is only called when committing styles so we // This function, however, is only called when committing styles so we
// need to ensure the cascade results are up-to-date manually. // need to ensure the cascade results are up-to-date manually.
EffectCompositor::MaybeUpdateCascadeResults(target->mElement, MaybeUpdateCascadeResults(target.mElement, target.mPseudoType);
target->mPseudoType);
EffectSet* effectSet = 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 // Get a list of effects sorted by composite order up to and including
// |aEffect|, even if it is not in the EffectSet. // |aEffect|, even if it is not in the EffectSet.
@ -483,9 +516,9 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel, ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
aAnimationValues); aAnimationValues);
MOZ_ASSERT(effectSet == MOZ_ASSERT(
EffectSet::GetEffectSet(target->mElement, target->mPseudoType), effectSet == EffectSet::GetEffectSet(target.mElement, target.mPseudoType),
"EffectSet should not change while composing style"); "EffectSet should not change while composing style");
return true; return true;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,33 +4,35 @@
#include "mozilla/KeyframeUtils.h" #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/ComputedStyle.h"
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
#include "mozilla/Move.h" #include "mozilla/Move.h"
#include "mozilla/RangedArray.h" #include "mozilla/RangedArray.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoBindingTypes.h" #include "mozilla/ServoBindingTypes.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoCSSParser.h" #include "mozilla/ServoCSSParser.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/TimingParams.h" #include "mozilla/TimingParams.h"
#include "mozilla/dom/BaseKeyframeTypesBinding.h" // For FastBaseKeyframe etc. #include "mozilla/dom/BaseKeyframeTypesBinding.h" // For FastBaseKeyframe etc.
#include "mozilla/dom/BindingCallContext.h"
#include "mozilla/dom/Document.h" // For Document::AreWebAnimationsImplicitKeyframesEnabled #include "mozilla/dom/Document.h" // For Document::AreWebAnimationsImplicitKeyframesEnabled
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/KeyframeEffect.h" // For PropertyValuesPair etc. #include "mozilla/dom/KeyframeEffect.h" // For PropertyValuesPair etc.
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/Nullable.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 "nsCSSPropertyIDSet.h"
#include "nsCSSProps.h" #include "nsCSSProps.h"
#include "nsCSSPseudoElements.h" // For PseudoStyleType #include "nsCSSPseudoElements.h" // For PseudoStyleType
#include "nsClassHashtable.h"
#include "nsContentUtils.h" // For GetContextForContent
#include "nsIScriptError.h" #include "nsIScriptError.h"
#include "nsPresContextInlines.h" #include "nsPresContextInlines.h"
#include "nsTArray.h" #include "nsTArray.h"
#include <algorithm> // For std::stable_sort, std::min
using mozilla::dom::Nullable; using mozilla::dom::Nullable;
@ -380,7 +382,10 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
const char* aContext, const char* aContext,
nsTArray<Keyframe>& aResult) { nsTArray<Keyframe>& aResult) {
JS::Rooted<JS::Value> value(aCx); 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 (;;) { for (;;) {
bool done; bool done;
@ -395,16 +400,20 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
// value). // value).
if (!value.isObject() && !value.isNullOrUndefined()) { if (!value.isObject() && !value.isNullOrUndefined()) {
dom::ThrowErrorMessage<dom::MSG_NOT_OBJECT>( dom::ThrowErrorMessage<dom::MSG_NOT_OBJECT>(
aCx, aCx, aContext, "Element of sequence<Keyframe> argument");
nsPrintfCString("%sElement of sequence<Keyframe> argument", aContext)
.get());
return false; return false;
} }
// Convert the JS value into a BaseKeyframe dictionary value. // Convert the JS value into a BaseKeyframe dictionary value.
dom::binding_detail::FastBaseKeyframe keyframeDict; 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")) { "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; return false;
} }
@ -412,6 +421,7 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
if (!keyframe) { if (!keyframe) {
return false; return false;
} }
if (!keyframeDict.mOffset.IsNull()) { if (!keyframeDict.mOffset.IsNull()) {
keyframe->mOffset.emplace(keyframeDict.mOffset.Value()); keyframe->mOffset.emplace(keyframeDict.mOffset.Value());
} }
@ -969,8 +979,8 @@ static void GetKeyframeListFromPropertyIndexedKeyframe(
// Convert the object to a property-indexed keyframe dictionary to // Convert the object to a property-indexed keyframe dictionary to
// get its explicit dictionary members. // get its explicit dictionary members.
dom::binding_detail::FastBasePropertyIndexedKeyframe keyframeDict; dom::binding_detail::FastBasePropertyIndexedKeyframe keyframeDict;
if (!keyframeDict.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument", // XXXbz Pass in the method name from callers and set up a BindingCallContext?
false)) { if (!keyframeDict.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument")) {
aRv.Throw(NS_ERROR_FAILURE); aRv.Throw(NS_ERROR_FAILURE);
return; return;
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -502,13 +502,14 @@ promise_test(t => {
var childBAnimations = childB.getAnimations(); var childBAnimations = childB.getAnimations();
var divBeforeAnimations = var divBeforeAnimations =
docAnims.filter(x => (x.effect.target.element == div && docAnims.filter(x => (x.effect.target == div &&
x.effect.target.type == "::before")); x.effect.pseudoElement == "::before"));
var divAfterAnimations = var divAfterAnimations =
docAnims.filter(x => (x.effect.target.element == div && docAnims.filter(x => (x.effect.target == div &&
x.effect.target.type == "::after")); x.effect.pseudoElement == "::after"));
var childBPseudoAnimations = var childBPseudoAnimations =
docAnims.filter(x => x.effect.target.element == childB); docAnims.filter(x => (x.effect.target == childB &&
x.effect.pseudoElement == "::before"));
var seekRecords; var seekRecords;
// The order in which we get the corresponding records is currently // 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() { function runTest() {
[ { subtree: false }, [ { subtree: false },
{ subtree: true } { subtree: true }
@ -1518,10 +1503,11 @@ function runTest() {
test(t => { test(t => {
var div = addDiv(t); var div = addDiv(t);
var pseudoTarget = createPseudo(t, div, 'before');
var observer = setupSynchronousObserver(t, div, true); 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(), assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }], [{ added: [anim], changed: [], removed: [] }],
@ -1561,13 +1547,13 @@ function runTest() {
test(t => { test(t => {
var div = addDiv(t); var div = addDiv(t);
var pseudoTarget = createPseudo(t, div, 'before');
var observer = setupSynchronousObserver(t, div, false); var observer = setupSynchronousObserver(t, div, false);
var anim = div.animate({ opacity: [ 0, 1 ] }, var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC }); { duration: 100 * MS_PER_SEC });
var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] }, var pAnim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC }); { duration: 100 * MS_PER_SEC,
pseudoElement: "::before" });
assert_equals_records(observer.takeRecords(), assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }], [{ added: [anim], changed: [], removed: [] }],

View File

@ -1644,6 +1644,27 @@ function testImportantRuleOverride() {
'compositor because any of the properties has important rules'); '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() { function start() {
var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1'] var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1']
.getService(SpecialPowers.Ci.nsIStringBundleService); .getService(SpecialPowers.Ci.nsIStringBundleService);
@ -1663,6 +1684,7 @@ function start() {
testTooLargeFrame(); testTooLargeFrame();
testTransformSVG(); testTransformSVG();
testImportantRuleOverride(); testImportantRuleOverride();
testCurrentColor();
promise_test(async t => { promise_test(async t => {
var div = addDiv(t, { class: 'compositable', 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 pref(dom.animations-api.implicit-keyframes.enabled,true) load 1467277-1.html
load 1524480-1.html load 1524480-1.html
load 1575926.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_distance_of_transform.html]
[mozilla/test_document_timeline_origin_time_range.html] [mozilla/test_document_timeline_origin_time_range.html]
[mozilla/test_hide_and_show.html] [mozilla/test_hide_and_show.html]
[mozilla/test_mainthread_synchronization_pref.html]
[mozilla/test_moz_prefixed_properties.html] [mozilla/test_moz_prefixed_properties.html]
[mozilla/test_pending_animation_tracker.html] [mozilla/test_pending_animation_tracker.html]
[mozilla/test_restyles.html] [mozilla/test_restyles.html]

View File

@ -117,6 +117,21 @@ test(t => {
); );
}, 'composite member is ignored on keyframes when using object notation'); }, '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(); done();
</script> </script>
</body> </body>

View File

@ -15,11 +15,6 @@ test(t => {
}, 'Document.getAnimations() is not available when getAnimations pref is' }, 'Document.getAnimations() is not available when getAnimations pref is'
+ ' disabled'); + ' disabled');
test(t => {
assert_false('CSSPseudoElement' in window);
}, 'CSSPseudoElement interface is not available when getAnimations pref is'
+ ' disabled');
done(); done();
</script> </script>
</body> </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() { add_task(async function restyling_for_animation_on_orphaned_element() {
const div = addDiv(null); const div = addDiv(null);
const animation = div.animate({ marginLeft: [ '0px', '100px' ] }, const animation = div.animate({ marginLeft: [ '0px', '100px' ] },
@ -1956,6 +1969,90 @@ waitForAllPaints(() => {
await ensureElementRemoval(div); 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> </script>

View File

@ -105,7 +105,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, 1e+39, 0, 0)'; div.style.transition = 'margin-left 100s cubic-bezier(0, 1e+39, 0, 0)';
flushComputedStyle(div); flushComputedStyle(div);
div.style.marginLeft = '0px'; 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)', 'cubic-bezier(0, ' + max_float + ', 0, 0)',
'y1 control point for CSS transition on upper boundary'); 'y1 control point for CSS transition on upper boundary');
div.style.transition = ''; div.style.transition = '';
@ -114,7 +114,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, 1e+39)'; div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, 1e+39)';
flushComputedStyle(div); flushComputedStyle(div);
div.style.marginLeft = '0px'; 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 + ')', 'cubic-bezier(0, 0, 0, ' + max_float + ')',
'y2 control point for CSS transition on upper boundary'); 'y2 control point for CSS transition on upper boundary');
div.style.transition = ''; div.style.transition = '';
@ -123,7 +123,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, -1e+39, 0, 0)'; div.style.transition = 'margin-left 100s cubic-bezier(0, -1e+39, 0, 0)';
flushComputedStyle(div); flushComputedStyle(div);
div.style.marginLeft = '0px'; 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)', 'cubic-bezier(0, ' + '-' + max_float + ', 0, 0)',
'y1 control point for CSS transition on lower boundary'); 'y1 control point for CSS transition on lower boundary');
div.style.transition = ''; div.style.transition = '';
@ -132,7 +132,7 @@ test(function(t) {
div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, -1e+39)'; div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, -1e+39)';
flushComputedStyle(div); flushComputedStyle(div);
div.style.marginLeft = '0px'; 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 + ')', 'cubic-bezier(0, 0, 0, ' + '-' + max_float + ')',
'y2 control point for CSS transition on lower boundary'); '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); RequestAudioFocus(aAgent);
AppendAgentAndIncreaseAgentsNum(aAgent); AppendAgentAndIncreaseAgentsNum(aAgent);
if (aAudible == AudibleState::eAudible) { AudioAudibleChanged(aAgent, aAudible,
AudioAudibleChanged(aAgent, AudibleState::eAudible, AudibleChangedReasons::eDataAudibleChanged);
AudibleChangedReasons::eDataAudibleChanged); if (IsEnableAudioCompetingForAllAgents() &&
} else if (IsEnableAudioCompetingForAllAgents() && aAudible != AudibleState::eAudible) {
aAudible != AudibleState::eAudible) {
NotifyAudioCompetingChanged(aAgent); NotifyAudioCompetingChanged(aAgent);
} }
} }

View File

@ -49,6 +49,41 @@ class AudioChannelService final : public nsIObserver {
NS_DECL_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 * eNotAudible : agent is not audible
* eMaybeAudible : agent is not audible now, but it might be audible later * eMaybeAudible : agent is not audible now, but it might be audible later
* eAudible : agent is audible now * eAudible : agent is audible now

View File

@ -14,6 +14,7 @@
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsINode.h" #include "nsINode.h"
#include "nsRange.h" #include "nsRange.h"
#include "nsTArray.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -42,6 +43,10 @@ template nsresult AbstractRange::SetStartAndEndInternal(
template nsresult AbstractRange::SetStartAndEndInternal( template nsresult AbstractRange::SetStartAndEndInternal(
const RawRangeBoundary& aStartBoundary, const RawRangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary, StaticRange* aRange); 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_ADDREF(AbstractRange)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange) NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange)
@ -68,12 +73,56 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AbstractRange) 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) AbstractRange::AbstractRange(nsINode* aNode)
: mIsPositioned(false), mIsGenerated(false), mCalledByJS(false) { : mIsPositioned(false), mIsGenerated(false), mCalledByJS(false) {
Init(aNode);
}
void AbstractRange::Init(nsINode* aNode) {
MOZ_ASSERT(aNode, "range isn't in a document!"); MOZ_ASSERT(aNode, "range isn't in a document!");
mOwner = aNode->OwnerDoc(); 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 { nsINode* AbstractRange::GetClosestCommonInclusiveAncestor() const {
return mIsPositioned ? nsContentUtils::GetClosestCommonInclusiveAncestor( return mIsPositioned ? nsContentUtils::GetClosestCommonInclusiveAncestor(
mStart.Container(), mEnd.Container()) mStart.Container(), mEnd.Container())

View File

@ -23,6 +23,11 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
AbstractRange() = delete; AbstractRange() = delete;
explicit AbstractRange(const AbstractRange& aOther) = 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_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbstractRange) 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<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange); 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; RefPtr<Document> mOwner;
RangeBoundary mStart; RangeBoundary mStart;
RangeBoundary mEnd; RangeBoundary mEnd;
@ -94,6 +115,8 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
bool mIsGenerated; bool mIsGenerated;
// Used by nsRange, but this should have this for minimizing the size. // Used by nsRange, but this should have this for minimizing the size.
bool mCalledByJS; bool mCalledByJS;
static bool sHasShutDown;
}; };
} // namespace dom } // 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_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Attr) 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) { void Attr::SetMap(nsDOMAttributeMap* aMap) {
if (mAttrMap && !aMap && sInitialized) { if (mAttrMap && !aMap && sInitialized) {
@ -116,7 +121,7 @@ nsresult Attr::SetOwnerDocument(Document* aDocument) {
Document* doc = OwnerDoc(); Document* doc = OwnerDoc();
NS_ASSERTION(doc != aDocument, "bad call to Attr::SetOwnerDocument"); NS_ASSERTION(doc != aDocument, "bad call to Attr::SetOwnerDocument");
doc->DeleteAllPropertiesFor(this); doc->RemoveAllPropertiesFor(this);
RefPtr<dom::NodeInfo> newNodeInfo = aDocument->NodeInfoManager()->GetNodeInfo( RefPtr<dom::NodeInfo> newNodeInfo = aDocument->NodeInfoManager()->GetNodeInfo(
mNodeInfo->NameAtom(), mNodeInfo->GetPrefixAtom(), mNodeInfo->NameAtom(), mNodeInfo->GetPrefixAtom(),
@ -171,8 +176,9 @@ void Attr::SetNodeValueInternal(const nsAString& aNodeValue,
nsresult Attr::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const { nsresult Attr::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
nsAutoString value; nsAutoString value;
const_cast<Attr*>(this)->GetValue(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); NS_ADDREF(*aResult);
return NS_OK; return NS_OK;

View File

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

View File

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

View File

@ -5,8 +5,8 @@
#include "mozilla/dom/BarProps.h" #include "mozilla/dom/BarProps.h"
#include "mozilla/dom/BarPropBinding.h" #include "mozilla/dom/BarPropBinding.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsDocShell.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "nsIScrollable.h"
#include "nsIWebBrowserChrome.h" #include "nsIWebBrowserChrome.h"
namespace mozilla { namespace mozilla {
@ -17,7 +17,7 @@ namespace dom {
// //
BarProp::BarProp(nsGlobalWindowInner* aWindow) : mDOMWindow(aWindow) {} BarProp::BarProp(nsGlobalWindowInner* aWindow) : mDOMWindow(aWindow) {}
BarProp::~BarProp() {} BarProp::~BarProp() = default;
nsPIDOMWindowInner* BarProp::GetParentObject() const { return mDOMWindow; } nsPIDOMWindowInner* BarProp::GetParentObject() const { return mDOMWindow; }
@ -88,7 +88,7 @@ already_AddRefed<nsIWebBrowserChrome> BarProp::GetBrowserChrome() {
MenubarProp::MenubarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {} MenubarProp::MenubarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
MenubarProp::~MenubarProp() {} MenubarProp::~MenubarProp() = default;
bool MenubarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) { bool MenubarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_MENUBAR, 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(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
ToolbarProp::~ToolbarProp() {} ToolbarProp::~ToolbarProp() = default;
bool ToolbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) { bool ToolbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_TOOLBAR, aRv); return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_TOOLBAR, aRv);
@ -125,7 +125,7 @@ void ToolbarProp::SetVisible(bool aVisible, CallerType aCallerType,
LocationbarProp::LocationbarProp(nsGlobalWindowInner* aWindow) LocationbarProp::LocationbarProp(nsGlobalWindowInner* aWindow)
: BarProp(aWindow) {} : BarProp(aWindow) {}
LocationbarProp::~LocationbarProp() {} LocationbarProp::~LocationbarProp() = default;
bool LocationbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) { bool LocationbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_LOCATIONBAR, return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_LOCATIONBAR,
@ -145,7 +145,7 @@ void LocationbarProp::SetVisible(bool aVisible, CallerType aCallerType,
PersonalbarProp::PersonalbarProp(nsGlobalWindowInner* aWindow) PersonalbarProp::PersonalbarProp(nsGlobalWindowInner* aWindow)
: BarProp(aWindow) {} : BarProp(aWindow) {}
PersonalbarProp::~PersonalbarProp() {} PersonalbarProp::~PersonalbarProp() = default;
bool PersonalbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) { bool PersonalbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR, 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(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
StatusbarProp::~StatusbarProp() {} StatusbarProp::~StatusbarProp() = default;
bool StatusbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) { bool StatusbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_STATUSBAR, aRv); return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_STATUSBAR, aRv);
@ -183,60 +183,24 @@ void StatusbarProp::SetVisible(bool aVisible, CallerType aCallerType,
ScrollbarsProp::ScrollbarsProp(nsGlobalWindowInner* aWindow) ScrollbarsProp::ScrollbarsProp(nsGlobalWindowInner* aWindow)
: BarProp(aWindow) {} : BarProp(aWindow) {}
ScrollbarsProp::~ScrollbarsProp() {} ScrollbarsProp::~ScrollbarsProp() = default;
bool ScrollbarsProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) { bool ScrollbarsProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
if (!mDOMWindow) { if (!mDOMWindow) {
return true; return true;
} }
nsCOMPtr<nsIScrollable> scroller = nsIDocShell* ds = mDOMWindow->GetDocShell();
do_QueryInterface(mDOMWindow->GetDocShell()); if (!ds) {
if (!scroller) {
return true; return true;
} }
int32_t prefValue; ScrollbarPreference pref = nsDocShell::Cast(ds)->ScrollbarPreference();
scroller->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, return pref != ScrollbarPreference::Never;
&prefValue);
if (prefValue != nsIScrollable::Scrollbar_Never) {
return true;
}
scroller->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
&prefValue);
return prefValue != nsIScrollable::Scrollbar_Never;
} }
void ScrollbarsProp::SetVisible(bool aVisible, CallerType aCallerType, void ScrollbarsProp::SetVisible(bool aVisible, CallerType, ErrorResult&) {
ErrorResult& aRv) { /* Do nothing */
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.
*/
} }
} // namespace dom } // namespace dom

View File

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

View File

@ -59,10 +59,7 @@ struct MOZ_STACK_CLASS BindContext final {
: nullptr), : nullptr),
mInComposedDoc(aParent.IsInComposedDoc()), mInComposedDoc(aParent.IsInComposedDoc()),
mInUncomposedDoc(aParent.IsInUncomposedDoc()), mInUncomposedDoc(aParent.IsInUncomposedDoc()),
mSubtreeRootChanges(true), mSubtreeRootChanges(true) {}
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aParent)) {}
// When re-binding a shadow host into a tree, we re-bind all the shadow tree // 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 // 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()), mBindingParent(aShadowRoot.Host()),
mInComposedDoc(aShadowRoot.IsInComposedDoc()), mInComposedDoc(aShadowRoot.IsInComposedDoc()),
mInUncomposedDoc(false), mInUncomposedDoc(false),
mSubtreeRootChanges(false), mSubtreeRootChanges(false) {}
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aShadowRoot)) {}
// This constructor is meant to be used when inserting native-anonymous // This constructor is meant to be used when inserting native-anonymous
// children into a subtree. // children into a subtree.
@ -88,10 +82,7 @@ struct MOZ_STACK_CLASS BindContext final {
mBindingParent(&aParentElement), mBindingParent(&aParentElement),
mInComposedDoc(aParentElement.IsInComposedDoc()), mInComposedDoc(aParentElement.IsInComposedDoc()),
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()), mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
mSubtreeRootChanges(true), mSubtreeRootChanges(true) {
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aParentElement)) {
MOZ_ASSERT(mInComposedDoc, "Binding NAC in a disconnected subtree?"); MOZ_ASSERT(mInComposedDoc, "Binding NAC in a disconnected subtree?");
} }
@ -101,27 +92,13 @@ struct MOZ_STACK_CLASS BindContext final {
mBindingParent(aBinding.GetBoundElement()), mBindingParent(aBinding.GetBoundElement()),
mInComposedDoc(aParentElement.IsInComposedDoc()), mInComposedDoc(aParentElement.IsInComposedDoc()),
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()), mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
mSubtreeRootChanges(true), mSubtreeRootChanges(true) {}
mCollectingDisplayedNodeDataDuringLoad(
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
aParentElement)) {}
bool CollectingDisplayedNodeDataDuringLoad() const {
return mCollectingDisplayedNodeDataDuringLoad;
}
private: private:
static bool IsLikelyUndisplayed(const nsINode& aParent) { static bool IsLikelyUndisplayed(const nsINode& aParent) {
return aParent.IsAnyOfHTMLElements(nsGkAtoms::style, nsGkAtoms::script); 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; Document& mDoc;
Element* const mBindingParent; 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 // Whether the bind operation will change the subtree root of the content
// we're binding. // we're binding.
const bool mSubtreeRootChanges; 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 } // namespace dom

View File

@ -735,7 +735,7 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
error.WouldReportJSException(); error.WouldReportJSException();
if (error.Failed()) { 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); MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
} }
BodyStream::~BodyStream() {} BodyStream::~BodyStream() = default;
void BodyStream::ErrorPropagation(JSContext* aCx, void BodyStream::ErrorPropagation(JSContext* aCx,
const MutexAutoLock& aProofOfLock, const MutexAutoLock& aProofOfLock,
@ -337,10 +337,16 @@ void BodyStream::ErrorPropagation(JSContext* aCx,
} }
// Let's use a generic error. // 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); 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); MutexAutoUnlock unlock(mMutex);
JS::ReadableStreamError(aCx, aStream, errorValue); JS::ReadableStreamError(aCx, aStream, errorValue);
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -17,12 +17,13 @@ using namespace dom;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
Comment::~Comment() {} Comment::~Comment() = default;
already_AddRefed<CharacterData> Comment::CloneDataNode( already_AddRefed<CharacterData> Comment::CloneDataNode(
mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const { mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const {
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; 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) { if (aCloneText) {
it->mText = mText; 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(nsIGlobalObject* aParent) : mParent(aParent) {}
Crypto::~Crypto() {} Crypto::~Crypto() = default;
/* virtual */ /* virtual */
JSObject* Crypto::WrapObject(JSContext* aCx, JSObject* Crypto::WrapObject(JSContext* aCx,
@ -58,7 +58,7 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
return; return;
} }
aArray.ComputeLengthAndData(); aArray.ComputeState();
uint32_t dataLen = aArray.Length(); uint32_t dataLen = aArray.Length();
if (dataLen == 0) { if (dataLen == 0) {
NS_WARNING("ArrayBufferView length is 0, cannot continue"); NS_WARNING("ArrayBufferView length is 0, cannot continue");

View File

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

View File

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

View File

@ -158,7 +158,8 @@ nsresult DOMImplementation::CreateHTMLDocument(const nsAString& aTitle,
rv = head->AppendChildTo(title, false); rv = head->AppendChildTo(title, false);
NS_ENSURE_SUCCESS(rv, rv); 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); rv = titleText->SetText(aTitle, false);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = title->AppendChildTo(titleText, false); rv = title->AppendChildTo(titleText, false);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -274,11 +274,14 @@ static bool AncestorChainCrossesShadowBoundary(nsIContent* aDescendant,
* test for it separately, e.g. with DoesNotAffectDirectionOfAncestors. * test for it separately, e.g. with DoesNotAffectDirectionOfAncestors.
* It *does* include textarea, because even if a textarea has dir=auto, it has * It *does* include textarea, because even if a textarea has dir=auto, it has
* unicode-bidi: plaintext and is handled automatically in bidi resolution. * 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) { static bool DoesNotParticipateInAutoDirection(const nsIContent* aContent) {
mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo(); mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo();
return ((!aContent->IsHTMLElement() || nodeInfo->Equals(nsGkAtoms::script) || return ((!aContent->IsHTMLElement() || nodeInfo->Equals(nsGkAtoms::script) ||
nodeInfo->Equals(nsGkAtoms::style) || nodeInfo->Equals(nsGkAtoms::style) ||
nodeInfo->Equals(nsGkAtoms::input) ||
nodeInfo->Equals(nsGkAtoms::textarea) || nodeInfo->Equals(nsGkAtoms::textarea) ||
aContent->IsInAnonymousSubtree())) && aContent->IsInAnonymousSubtree())) &&
!aContent->IsShadowRoot(); !aContent->IsShadowRoot();
@ -506,9 +509,7 @@ class nsTextNodeDirectionalityMap {
aTextNode->SetHasTextNodeDirectionalityMap(); aTextNode->SetHasTextNodeDirectionalityMap();
} }
~nsTextNodeDirectionalityMap() { MOZ_COUNTED_DTOR(nsTextNodeDirectionalityMap)
MOZ_COUNT_DTOR(nsTextNodeDirectionalityMap);
}
static void nsTextNodeDirectionalityMapPropertyDestructor( static void nsTextNodeDirectionalityMapPropertyDestructor(
void* aObject, nsAtom* aProperty, void* aPropertyValue, void* aData) { void* aObject, nsAtom* aProperty, void* aPropertyValue, void* aData) {
@ -536,7 +537,7 @@ class nsTextNodeDirectionalityMap {
mElements.Remove(aElement); mElements.Remove(aElement);
aElement->ClearHasDirAutoSet(); aElement->ClearHasDirAutoSet();
aElement->DeleteProperty(nsGkAtoms::dirAutoSetBy); aElement->RemoveProperty(nsGkAtoms::dirAutoSetBy);
} }
void RemoveEntryForProperty(Element* aElement) { void RemoveEntryForProperty(Element* aElement) {
@ -602,7 +603,7 @@ class nsTextNodeDirectionalityMap {
nsTextNodeDirectionalityMap::AddEntryToMap(newTextNode, rootNode); nsTextNodeDirectionalityMap::AddEntryToMap(newTextNode, rootNode);
} else { } else {
rootNode->ClearHasDirAutoSet(); rootNode->ClearHasDirAutoSet();
rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy); rootNode->RemoveProperty(nsGkAtoms::dirAutoSetBy);
} }
return OpRemove; return OpRemove;
} }
@ -631,7 +632,7 @@ class nsTextNodeDirectionalityMap {
mElements.EnumerateEntries(TakeEntries, &entries); mElements.EnumerateEntries(TakeEntries, &entries);
for (Element* el : entries) { for (Element* el : entries) {
el->ClearHasDirAutoSet(); 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) { : mKey(aKey), mTabGroup(aTabGroup) {
// This method does not add itself to mTabGroup->mDocGroups as the caller does // This method does not add itself to mTabGroup->mDocGroups as the caller does
// it for us. // it for us.
if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
mArena = new mozilla::dom::DOMArena();
}
mPerformanceCounter = mPerformanceCounter =
new mozilla::PerformanceCounter(NS_LITERAL_CSTRING("DocGroup:") + aKey); new mozilla::PerformanceCounter(NS_LITERAL_CSTRING("DocGroup:") + aKey);
} }
@ -60,6 +64,8 @@ DocGroup::~DocGroup() {
nsIEventTarget* target = EventTargetFor(TaskCategory::Other); nsIEventTarget* target = EventTargetFor(TaskCategory::Other);
NS_ProxyRelease("DocGroup::mReactionsStack", target, NS_ProxyRelease("DocGroup::mReactionsStack", target,
mReactionsStack.forget()); mReactionsStack.forget());
NS_ProxyRelease("DocGroup::mArena", target, mArena.forget());
} }
mTabGroup->mDocGroups.RemoveEntry(mKey); mTabGroup->mDocGroups.RemoveEntry(mKey);
@ -144,7 +150,7 @@ RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
->Then( ->Then(
mainThread, __func__, mainThread, __func__,
[self, host, pid, windowID, duration, isTopLevel, [self, host, pid, windowID, duration, isTopLevel,
items](const PerformanceMemoryInfo& aMemoryInfo) { items = std::move(items)](const PerformanceMemoryInfo& aMemoryInfo) {
PerformanceInfo info = PerformanceInfo info =
PerformanceInfo(host, pid, windowID, duration, PerformanceInfo(host, pid, windowID, duration,
self->mPerformanceCounter->GetID(), false, self->mPerformanceCounter->GetID(), false,

View File

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

View File

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

View File

@ -71,35 +71,44 @@ StyleSheetList* DocumentOrShadowRoot::StyleSheets() {
} }
void DocumentOrShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) { void DocumentOrShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
aSheet.SetAssociatedDocumentOrShadowRoot( aSheet.SetAssociatedDocumentOrShadowRoot(this);
this, StyleSheet::OwnedByDocumentOrShadowRoot);
mStyleSheets.InsertElementAt(aIndex, &aSheet); mStyleSheets.InsertElementAt(aIndex, &aSheet);
} }
void DocumentOrShadowRoot::InsertAdoptedSheetAt(size_t aIndex, void DocumentOrShadowRoot::RemoveStyleSheet(StyleSheet& aSheet) {
StyleSheet& aSheet) {
mAdoptedStyleSheets.InsertElementAt(aIndex, &aSheet);
}
already_AddRefed<StyleSheet> DocumentOrShadowRoot::RemoveSheet(
StyleSheet& aSheet) {
auto index = mStyleSheets.IndexOf(&aSheet); auto index = mStyleSheets.IndexOf(&aSheet);
if (index == mStyleSheets.NoIndex) { 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]); RefPtr<StyleSheet> sheet = std::move(mStyleSheets[index]);
mStyleSheets.RemoveElementAt(index); mStyleSheets.RemoveElementAt(index);
RemoveSheetFromStylesIfApplicable(*sheet);
sheet->ClearAssociatedDocumentOrShadowRoot(); 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 // https://wicg.github.io/construct-stylesheets/#dom-documentorshadowroot-adoptedstylesheets
void DocumentOrShadowRoot::EnsureAdoptedSheetsAreValid( void DocumentOrShadowRoot::SetAdoptedStyleSheets(
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets, const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
ErrorResult& aRv) { ErrorResult& aRv) {
nsTHashtable<nsPtrHashKey<const StyleSheet>> set( Document& doc = *AsNode().OwnerDoc();
aAdoptedStyleSheets.Length());
for (const OwningNonNull<StyleSheet>& sheet : aAdoptedStyleSheets) { for (const OwningNonNull<StyleSheet>& sheet : aAdoptedStyleSheets) {
// 2.1 Check if all sheets are constructed, else throw NotAllowedError // 2.1 Check if all sheets are constructed, else throw NotAllowedError
if (!sheet->IsConstructed()) { if (!sheet->IsConstructed()) {
@ -109,23 +118,117 @@ void DocumentOrShadowRoot::EnsureAdoptedSheetsAreValid(
} }
// 2.2 Check if all sheets' constructor documents match the // 2.2 Check if all sheets' constructor documents match the
// DocumentOrShadowRoot's node document, else throw NotAlloweError // DocumentOrShadowRoot's node document, else throw NotAlloweError
if (!sheet->ConstructorDocumentMatches(AsNode().OwnerDoc())) { if (!sheet->ConstructorDocumentMatches(doc)) {
return aRv.ThrowNotAllowedError( return aRv.ThrowNotAllowedError(
"Each adopted style sheet's constructor document must match the " "Each adopted style sheet's constructor document must match the "
"document or shadow root's node document"); "document or shadow root's node document");
} }
}
// FIXME(nordzilla): This is temporary code to disallow duplicate sheets. auto* shadow = ShadowRoot::FromNode(AsNode());
// This exists to ensure that the fuzzers aren't blocked. MOZ_ASSERT((mKind == Kind::ShadowRoot) == !!shadow);
// This code will eventually be removed.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1617302 StyleSheetSet set(aAdoptedStyleSheets.Length());
if (!set.EnsureInserted(sheet.get())) { size_t commonPrefix = 0;
return aRv.ThrowNotAllowedError(
"Temporarily disallowing duplicate stylesheets."); // 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) { Element* DocumentOrShadowRoot::GetElementById(const nsAString& aElementId) {
if (MOZ_UNLIKELY(aElementId.IsEmpty())) { if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc()); nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
@ -184,25 +287,13 @@ nsIContent* DocumentOrShadowRoot::Retarget(nsIContent* aContent) const {
} }
Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() { Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) { auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; if (!content) {
nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant( return nullptr;
window, nsFocusManager::eOnlyCurrentWindow, }
getter_AddRefs(focusedWindow)); if (nsIContent* retarget = Retarget(content)) {
// be safe and make sure the element is from this document return retarget->AsElement();
if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
if (focusedContent->ChromeOnlyAccess()) {
focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
}
if (focusedContent) {
if (nsIContent* retarget = Retarget(focusedContent)) {
return retarget->AsElement();
}
}
}
} }
return nullptr; return nullptr;
} }
@ -225,7 +316,7 @@ Element* DocumentOrShadowRoot::GetFullscreenElement() {
return nullptr; return nullptr;
} }
Element* element = AsNode().OwnerDoc()->FullscreenStackTop(); Element* element = AsNode().OwnerDoc()->GetUnretargetedFullScreenElement();
NS_ASSERTION(!element || element->State().HasState(NS_EVENT_STATE_FULLSCREEN), NS_ASSERTION(!element || element->State().HasState(NS_EVENT_STATE_FULLSCREEN),
"Fullscreen element should have fullscreen styles applied"); "Fullscreen element should have fullscreen styles applied");
@ -510,7 +601,7 @@ void DocumentOrShadowRoot::GetAnimations(
child = child->GetNextSibling()) { child = child->GetNextSibling()) {
if (RefPtr<Element> element = Element::FromNode(child)) { if (RefPtr<Element> element = Element::FromNode(child)) {
nsTArray<RefPtr<Animation>> result; nsTArray<RefPtr<Animation>> result;
element->GetAnimations(options, result, Element::Flush::No); element->GetAnimationsWithoutFlush(options, result);
aAnimations.AppendElements(std::move(result)); aAnimations.AppendElements(std::move(result));
} }
} }
@ -660,7 +751,9 @@ nsRadioGroupStruct* DocumentOrShadowRoot::GetOrCreateRadioGroup(
int32_t DocumentOrShadowRoot::StyleOrderIndexOfSheet( int32_t DocumentOrShadowRoot::StyleOrderIndexOfSheet(
const StyleSheet& aSheet) const { const StyleSheet& aSheet) const {
if (aSheet.IsConstructed()) { 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 (index < 0) ? index : index + SheetCount();
} }
return mStyleSheets.IndexOf(&aSheet); return mStyleSheets.IndexOf(&aSheet);
@ -677,26 +770,27 @@ void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdoptedStyleSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdoptedStyleSheets)
auto NoteSheets = [tmp, &cb = cb](nsTArray<RefPtr<StyleSheet>>& sheetList) { auto NoteSheetIfApplicable = [&](StyleSheet& aSheet) {
for (StyleSheet* sheet : sheetList) { if (!aSheet.IsApplicable()) {
if (!sheet->IsApplicable()) { return;
continue; }
} // The style set or mServoStyles keep more references to it if the sheet
// The style set or mServoStyles keep more references to it if the sheet // is applicable.
// is applicable. if (tmp->mKind == Kind::ShadowRoot) {
if (tmp->mKind == Kind::ShadowRoot) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]"); cb.NoteXPCOMChild(&aSheet);
cb.NoteXPCOMChild(sheet); } else if (tmp->AsNode().AsDocument()->StyleSetFilled()) {
} else if (tmp->AsNode().AsDocument()->StyleSetFilled()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME( cb, "mStyleSet->mRawSet.stylist.stylesheets.author[i]");
cb, "mStyleSet->mRawSet.stylist.stylesheets.author[i]"); cb.NoteXPCOMChild(&aSheet);
cb.NoteXPCOMChild(sheet);
}
} }
}; };
NoteSheets(tmp->mStyleSheets); for (auto& sheet : tmp->mStyleSheets) {
NoteSheets(tmp->mAdoptedStyleSheets); NoteSheetIfApplicable(*sheet);
}
tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront(NoteSheetIfApplicable);
for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) { for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) {
iter.Get()->Traverse(&cb); iter.Get()->Traverse(&cb);
@ -719,7 +813,12 @@ void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) { void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets); 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); sheet->RemoveAdopter(*tmp);
} }
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets); NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);

View File

@ -86,6 +86,8 @@ class DocumentOrShadowRoot {
void GetAdoptedStyleSheets(nsTArray<RefPtr<StyleSheet>>&) const; void GetAdoptedStyleSheets(nsTArray<RefPtr<StyleSheet>>&) const;
void RemoveStyleSheet(StyleSheet&);
Element* GetElementById(const nsAString& aElementId); Element* GetElementById(const nsAString& aElementId);
/** /**
@ -219,16 +221,38 @@ class DocumentOrShadowRoot {
nsIContent* Retarget(nsIContent* aContent) const; nsIContent* Retarget(nsIContent* aContent) const;
protected: void SetAdoptedStyleSheets(
// 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(
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets, const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
ErrorResult& aRv); 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 AddSizeOfExcludingThis(nsWindowSizes&) const;
void AddSizeOfOwnedSheetArrayExcludingThis( void AddSizeOfOwnedSheetArrayExcludingThis(
nsWindowSizes&, const nsTArray<RefPtr<StyleSheet>>&) const; nsWindowSizes&, const nsTArray<RefPtr<StyleSheet>>&) const;
@ -243,8 +267,10 @@ class DocumentOrShadowRoot {
nsTArray<RefPtr<StyleSheet>> mStyleSheets; nsTArray<RefPtr<StyleSheet>> mStyleSheets;
RefPtr<StyleSheetList> mDOMStyleSheets; 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; nsTArray<RefPtr<StyleSheet>> mAdoptedStyleSheets;
/* /*

View File

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

View File

@ -24,6 +24,7 @@
#include "mozilla/dom/MutationObservers.h" #include "mozilla/dom/MutationObservers.h"
#include "mozilla/dom/ScriptLoader.h" #include "mozilla/dom/ScriptLoader.h"
#include "mozilla/dom/Text.h" #include "mozilla/dom/Text.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/gfx/Matrix.h" #include "mozilla/gfx/Matrix.h"
#include "nsAtom.h" #include "nsAtom.h"
#include "nsDOMAttributeMap.h" #include "nsDOMAttributeMap.h"
@ -260,8 +261,7 @@ Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) {
} }
EventStates Element::IntrinsicState() const { EventStates Element::IntrinsicState() const {
return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE return IsEditable() ? NS_EVENT_STATE_READWRITE : NS_EVENT_STATE_READONLY;
: NS_EVENT_STATE_MOZ_READONLY;
} }
void Element::NotifyStateChange(EventStates aStates) { void Element::NotifyStateChange(EventStates aStates) {
@ -299,13 +299,26 @@ void Element::UpdateState(bool aNotify) {
} // namespace mozilla } // namespace mozilla
void nsIContent::UpdateEditableState(bool aNotify) { 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. // We allow setting the flag on NAC (explicitly, see
// This needs to be set explicitly, see for example // nsTextControlFrame::CreateAnonymousContent for example), but not
// nsTextControlFrame::CreateRootNode(). // unsetting it.
SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE) && //
!IsRootOfNativeAnonymousSubtree()); // 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 { namespace mozilla {
@ -321,19 +334,28 @@ void Element::UpdateEditableState(bool aNotify) {
// insertion into the document and UpdateState can be slow for // insertion into the document and UpdateState can be slow for
// some kinds of elements even when not notifying. // some kinds of elements even when not notifying.
if (IsEditable()) { if (IsEditable()) {
RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY); RemoveStatesSilently(NS_EVENT_STATE_READONLY);
AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); AddStatesSilently(NS_EVENT_STATE_READWRITE);
} else { } else {
RemoveStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); RemoveStatesSilently(NS_EVENT_STATE_READWRITE);
AddStatesSilently(NS_EVENT_STATE_MOZ_READONLY); AddStatesSilently(NS_EVENT_STATE_READONLY);
} }
} }
} }
int32_t Element::TabIndex() { Maybe<int32_t> Element::GetTabIndexAttrValue() {
const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::tabindex); const nsAttrValue* attrVal = GetParsedAttr(nsGkAtoms::tabindex);
if (attrVal && attrVal->Type() == nsAttrValue::eInteger) { 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(); return TabIndexDefault();
@ -341,21 +363,21 @@ int32_t Element::TabIndex() {
void Element::Focus(const FocusOptions& aOptions, ErrorResult& aError) { void Element::Focus(const FocusOptions& aOptions, ErrorResult& aError) {
nsFocusManager* fm = nsFocusManager::GetFocusManager(); nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm) {
return;
}
// Also other browsers seem to have the hack to not re-focus (and flush) when // Also other browsers seem to have the hack to not re-focus (and flush) when
// the element is already focused. // the element is already focused.
// Until https://github.com/whatwg/html/issues/4512 is clarified, we'll // Until https://github.com/whatwg/html/issues/4512 is clarified, we'll
// maintain interoperatibility by not re-focusing, independent of aOptions. // maintain interoperatibility by not re-focusing, independent of aOptions.
// I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll: // I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll:
// false })` won't re-focus. // false })` won't re-focus.
if (fm) { if (fm->CanSkipFocus(this)) {
if (fm->CanSkipFocus(this)) { fm->NeedsFlushBeforeEventHandling(this);
fm->NeedsFlushBeforeEventHandling(this); return;
} else {
aError = fm->SetFocus(
this, nsIFocusManager::FLAG_BYELEMENTFOCUS |
nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions));
}
} }
aError = fm->SetFocus(
this, nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions));
} }
void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) { void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) {
@ -493,7 +515,7 @@ void Element::UnlockStyleStates(EventStates aStates) {
locks->mLocks &= ~aStates; locks->mLocks &= ~aStates;
if (locks->mLocks.IsEmpty()) { if (locks->mLocks.IsEmpty()) {
DeleteProperty(nsGkAtoms::lockedStyleStates); RemoveProperty(nsGkAtoms::lockedStyleStates);
ClearHasLockedStyleStates(); ClearHasLockedStyleStates();
delete locks; delete locks;
} else { } else {
@ -507,7 +529,7 @@ void Element::UnlockStyleStates(EventStates aStates) {
void Element::ClearStyleStateLocks() { void Element::ClearStyleStateLocks() {
StyleStateLocks locks = LockedStyleStates(); StyleStateLocks locks = LockedStyleStates();
DeleteProperty(nsGkAtoms::lockedStyleStates); RemoveProperty(nsGkAtoms::lockedStyleStates);
ClearHasLockedStyleStates(); ClearHasLockedStyleStates();
NotifyStyleStateChange(locks.mLocks); NotifyStyleStateChange(locks.mLocks);
@ -967,7 +989,7 @@ nsRect Element::GetClientAreaRect() {
// document, we have overlay scrollbars, and we aren't embedded in another // document, we have overlay scrollbars, and we aren't embedded in another
// document // document
bool overlayScrollbars = bool overlayScrollbars =
LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0; LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0;
bool rootContentDocument = bool rootContentDocument =
presContext && presContext->IsRootContentDocument(); presContext && presContext->IsRootContentDocument();
if (overlayScrollbars && rootContentDocument && if (overlayScrollbars && rootContentDocument &&
@ -1218,8 +1240,9 @@ already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
* context object's node document, host is context object, * context object's node document, host is context object,
* and mode is init's mode. * and mode is init's mode.
*/ */
auto* nim = nodeInfo->NodeInfoManager();
RefPtr<ShadowRoot> shadowRoot = RefPtr<ShadowRoot> shadowRoot =
new ShadowRoot(this, aMode, nodeInfo.forget()); new (nim) ShadowRoot(this, aMode, nodeInfo.forget());
if (NodeOrAncestorHasDirAuto()) { if (NodeOrAncestorHasDirAuto()) {
shadowRoot->SetAncestorHasDirAuto(); 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 // Now recurse into our kids. Ensure this happens after binding the shadow
// root so that directionality of slots is updated. // root so that directionality of slots is updated.
{ {
BindContext::NestingLevel level(aContext, *this);
for (nsIContent* child = GetFirstChild(); child; for (nsIContent* child = GetFirstChild(); child;
child = child->GetNextSibling()) { child = child->GetNextSibling()) {
rv = child->BindToTree(aContext, *this); rv = child->BindToTree(aContext, *this);
@ -1911,14 +1933,14 @@ void Element::UnbindFromTree(bool aNullParent) {
// //
// FIXME (Bug 522599): Need a test for this. // FIXME (Bug 522599): Need a test for this.
if (MayHaveAnimations()) { if (MayHaveAnimations()) {
DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty); RemoveProperty(nsGkAtoms::transitionsOfBeforeProperty);
DeleteProperty(nsGkAtoms::transitionsOfAfterProperty); RemoveProperty(nsGkAtoms::transitionsOfAfterProperty);
DeleteProperty(nsGkAtoms::transitionsOfMarkerProperty); RemoveProperty(nsGkAtoms::transitionsOfMarkerProperty);
DeleteProperty(nsGkAtoms::transitionsProperty); RemoveProperty(nsGkAtoms::transitionsProperty);
DeleteProperty(nsGkAtoms::animationsOfBeforeProperty); RemoveProperty(nsGkAtoms::animationsOfBeforeProperty);
DeleteProperty(nsGkAtoms::animationsOfAfterProperty); RemoveProperty(nsGkAtoms::animationsOfAfterProperty);
DeleteProperty(nsGkAtoms::animationsOfMarkerProperty); RemoveProperty(nsGkAtoms::animationsOfMarkerProperty);
DeleteProperty(nsGkAtoms::animationsProperty); RemoveProperty(nsGkAtoms::animationsProperty);
if (document) { if (document) {
if (nsPresContext* presContext = document->GetPresContext()) { if (nsPresContext* presContext = document->GetPresContext()) {
// We have to clear all pending restyle requests for the animations on // 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::IsLabelable() const { return false; }
bool Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const { bool Element::IsInteractiveHTMLContent() const { return false; }
return false;
}
DeclarationBlock* Element::GetInlineStyleDeclaration() const { DeclarationBlock* Element::GetInlineStyleDeclaration() const {
if (!MayHaveStyle()) { if (!MayHaveStyle()) {
@ -2613,8 +2633,7 @@ bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
return true; return true;
} }
if (aAttribute == nsGkAtoms::exportparts && if (aAttribute == nsGkAtoms::exportparts) {
StaticPrefs::layout_css_shadow_parts_enabled()) {
aResult.ParsePartMapping(aValue); aResult.ParsePartMapping(aValue);
return true; return true;
} }
@ -3325,8 +3344,78 @@ CORSMode Element::AttrValueToCORSMode(const nsAttrValue* aValue) {
return CORSMode(aValue->GetEnumValue()); 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) { 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, 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); auto request = FullscreenRequest::Create(this, aCallerType, aRv);
RefPtr<Promise> promise = request->GetPromise(); 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 // 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). // 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, // 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() { already_AddRefed<Flex> Element::GetAsFlexContainer() {
nsIFrame* frame = GetPrimaryFrame();
// We need the flex frame to compute additional info, and use // We need the flex frame to compute additional info, and use
// that annotated version of the frame. // that annotated version of the frame.
nsFlexContainerFrame* flexFrame = nsFlexContainerFrame* flexFrame =
nsFlexContainerFrame::GetFlexFrameWithComputedInfo(frame); nsFlexContainerFrame::GetFlexFrameWithComputedInfo(
GetPrimaryFrame(FlushType::Layout));
if (flexFrame) { if (flexFrame) {
RefPtr<Flex> flex = new Flex(this, flexFrame); RefPtr<Flex> flex = new Flex(this, flexFrame);
@ -3376,7 +3458,8 @@ already_AddRefed<Flex> Element::GetAsFlexContainer() {
void Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult) { void Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult) {
nsGridContainerFrame* frame = nsGridContainerFrame* frame =
nsGridContainerFrame::GetGridFrameWithComputedInfo(GetPrimaryFrame()); nsGridContainerFrame::GetGridFrameWithComputedInfo(
GetPrimaryFrame(FlushType::Layout));
// If we get a nsGridContainerFrame from the prior call, // If we get a nsGridContainerFrame from the prior call,
// all the next-in-flow frames will also be nsGridContainerFrames. // 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, JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError) { ErrorResult& aError) {
Nullable<ElementOrCSSPseudoElement> target; nsCOMPtr<nsIGlobalObject> ownerGlobal = GetOwnerGlobal();
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();
if (!ownerGlobal) { if (!ownerGlobal) {
aError.Throw(NS_ERROR_FAILURE); aError.Throw(NS_ERROR_FAILURE);
return nullptr; return nullptr;
@ -3476,8 +3537,8 @@ already_AddRefed<Animation> Element::Animate(
// convention and needs to be called in caller's compartment. // convention and needs to be called in caller's compartment.
// This should match to RunConstructorInCallerCompartment attribute in // This should match to RunConstructorInCallerCompartment attribute in
// KeyframeEffect.webidl. // KeyframeEffect.webidl.
RefPtr<KeyframeEffect> effect = KeyframeEffect::Constructor( RefPtr<KeyframeEffect> effect =
global, aTarget, aKeyframes, aOptions, aError); KeyframeEffect::Constructor(global, this, aKeyframes, aOptions, aError);
if (aError.Failed()) { if (aError.Failed()) {
return nullptr; return nullptr;
} }
@ -3486,7 +3547,7 @@ already_AddRefed<Animation> Element::Animate(
// needs to be called in the target element's realm. // needs to be called in the target element's realm.
JSAutoRealm ar(aContext, global.Get()); JSAutoRealm ar(aContext, global.Get());
AnimationTimeline* timeline = referenceElement->OwnerDoc()->Timeline(); AnimationTimeline* timeline = OwnerDoc()->Timeline();
RefPtr<Animation> animation = Animation::Constructor( RefPtr<Animation> animation = Animation::Constructor(
global, effect, Optional<AnimationTimeline*>(timeline), aError); global, effect, Optional<AnimationTimeline*>(timeline), aError);
if (aError.Failed()) { if (aError.Failed()) {
@ -3506,23 +3567,26 @@ already_AddRefed<Animation> Element::Animate(
} }
void Element::GetAnimations(const GetAnimationsOptions& aOptions, void Element::GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations, nsTArray<RefPtr<Animation>>& aAnimations) {
Flush aFlush) { if (Document* doc = GetComposedDoc()) {
if (aFlush == Flush::Yes) { // We don't need to explicitly flush throttled animations here, since
if (Document* doc = GetComposedDoc()) { // updating the animation style of elements will never affect the set of
// We don't need to explicitly flush throttled animations here, since // running animations and it's only the set of running animations that is
// updating the animation style of elements will never affect the set of // important here.
// 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
// NOTE: Any changes to the flags passed to the following call should // too.
// be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations doc->FlushPendingNotifications(
// too. ChangesToFlush(FlushType::Style, false /* flush animations */));
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
}
} }
GetAnimationsWithoutFlush(aOptions, aAnimations);
}
void Element::GetAnimationsWithoutFlush(
const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations) {
Element* elem = this; Element* elem = this;
PseudoStyleType pseudoType = PseudoStyleType::NotPseudo; PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
// For animations on generated-content elements, the animations are stored // 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; localName = nsGkAtoms::body;
namespaceID = kNameSpaceID_XHTML; namespaceID = kNameSpaceID_XHTML;
} }
RefPtr<DocumentFragment> fragment = RefPtr<DocumentFragment> fragment = new (OwnerDoc()->NodeInfoManager())
new DocumentFragment(OwnerDoc()->NodeInfoManager()); DocumentFragment(OwnerDoc()->NodeInfoManager());
nsContentUtils::ParseFragmentHTML( nsContentUtils::ParseFragmentHTML(
aOuterHTML, fragment, localName, namespaceID, aOuterHTML, fragment, localName, namespaceID,
OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks, true); OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
@ -3869,15 +3933,32 @@ float Element::FontSizeInflation() {
return 1.0; 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()) { if (IsHTMLElement()) {
const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrerpolicy); return ReferrerPolicyFromAttr(GetParsedAttr(nsGkAtoms::referrerpolicy));
return ReferrerPolicyFromAttr(referrerValue);
} }
return ReferrerPolicy::_empty; return ReferrerPolicy::_empty;
} }
ReferrerPolicy Element::ReferrerPolicyFromAttr(const nsAttrValue* aValue) { ReferrerPolicy Element::ReferrerPolicyFromAttr(
const nsAttrValue* aValue) const {
if (aValue && aValue->Type() == nsAttrValue::eEnum) { if (aValue && aValue->Type() == nsAttrValue::eEnum) {
return ReferrerPolicy(aValue->GetEnumValue()); return ReferrerPolicy(aValue->GetEnumValue());
} }
@ -3952,14 +4033,14 @@ void Element::UnregisterIntersectionObserver(
if (observers) { if (observers) {
observers->Remove(aObserver); observers->Remove(aObserver);
if (observers->IsEmpty()) { if (observers->IsEmpty()) {
DeleteProperty(nsGkAtoms::intersectionobserverlist); RemoveProperty(nsGkAtoms::intersectionobserverlist);
} }
} }
} }
void Element::UnlinkIntersectionObservers() { void Element::UnlinkIntersectionObservers() {
// IntersectionObserverPropertyDtor takes care of the hard work. // IntersectionObserverPropertyDtor takes care of the hard work.
DeleteProperty(nsGkAtoms::intersectionobserverlist); RemoveProperty(nsGkAtoms::intersectionobserverlist);
} }
bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver,

View File

@ -17,16 +17,15 @@
#include "nsChangeHint.h" #include "nsChangeHint.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsDOMAttributeMap.h" #include "nsDOMAttributeMap.h"
#include "nsStyleConsts.h"
#include "nsINodeList.h" #include "nsINodeList.h"
#include "nsIScrollableFrame.h" #include "nsIScrollableFrame.h"
#include "nsPresContext.h"
#include "Units.h" #include "Units.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/CORSMode.h" #include "mozilla/CORSMode.h"
#include "mozilla/EventForwards.h" #include "mozilla/EventForwards.h"
#include "mozilla/EventStates.h" #include "mozilla/EventStates.h"
#include "mozilla/FlushType.h" #include "mozilla/FlushType.h"
#include "mozilla/PresShell.h"
#include "mozilla/PseudoStyleType.h" #include "mozilla/PseudoStyleType.h"
#include "mozilla/RustCell.h" #include "mozilla/RustCell.h"
#include "mozilla/SMILAttr.h" #include "mozilla/SMILAttr.h"
@ -162,7 +161,7 @@ class Element : public FragmentOrElement {
#ifdef MOZILLA_INTERNAL_API #ifdef MOZILLA_INTERNAL_API
explicit Element(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) explicit Element(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
: FragmentOrElement(std::move(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, MOZ_ASSERT(mNodeInfo->NodeType() == ELEMENT_NODE,
"Bad NodeType in aNodeInfo"); "Bad NodeType in aNodeInfo");
SetIsElement(); SetIsElement();
@ -216,6 +215,11 @@ class Element : public FragmentOrElement {
*/ */
int32_t TabIndex(); int32_t TabIndex();
/**
* Get the parsed value of tabindex attribute.
*/
Maybe<int32_t> GetTabIndexAttrValue();
/** /**
* Set tabIndex value to this element. * 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. * 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 * Returns |this| as an nsIMozBrowserFrame* if the element is a frame or
@ -1180,31 +1184,11 @@ class Element : public FragmentOrElement {
} }
return false; return false;
} }
void SetCapture(bool aRetargetToElement) { 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 SetCaptureAlways(bool aRetargetToElement) { void SetCaptureAlways(bool aRetargetToElement);
PresShell::SetCapturingContent(
this, CaptureFlags::PreventDragStart |
CaptureFlags::IgnoreAllowedState |
(aRetargetToElement ? CaptureFlags::RetargetToElement
: CaptureFlags::None));
}
void ReleaseCapture() { void ReleaseCapture();
if (PresShell::GetCapturingContent() == this) {
PresShell::ReleaseCapturingContent();
}
}
already_AddRefed<Promise> RequestFullscreen(CallerType, ErrorResult&); already_AddRefed<Promise> RequestFullscreen(CallerType, ErrorResult&);
void RequestPointerLock(CallerType aCallerType); void RequestPointerLock(CallerType aCallerType);
@ -1275,48 +1259,52 @@ class Element : public FragmentOrElement {
MOZ_CAN_RUN_SCRIPT int32_t ScrollHeight(); MOZ_CAN_RUN_SCRIPT int32_t ScrollHeight();
MOZ_CAN_RUN_SCRIPT void MozScrollSnap(); MOZ_CAN_RUN_SCRIPT void MozScrollSnap();
MOZ_CAN_RUN_SCRIPT int32_t ClientTop() { 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() { 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() { 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() { 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() { MOZ_CAN_RUN_SCRIPT int32_t ScrollTopMin() {
nsIScrollableFrame* sf = GetScrollFrame(); nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().y) if (!sf) {
: 0; return 0;
}
return CSSPixel::FromAppUnits(sf->GetScrollRange().y).Rounded();
} }
MOZ_CAN_RUN_SCRIPT int32_t ScrollTopMax() { MOZ_CAN_RUN_SCRIPT int32_t ScrollTopMax() {
nsIScrollableFrame* sf = GetScrollFrame(); nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels( if (!sf) {
sf->GetScrollRange().YMost()) return 0;
: 0; }
return CSSPixel::FromAppUnits(sf->GetScrollRange().YMost()).Rounded();
} }
MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMin() { MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMin() {
nsIScrollableFrame* sf = GetScrollFrame(); nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().x) if (!sf) {
: 0; return 0;
}
return CSSPixel::FromAppUnits(sf->GetScrollRange().x).Rounded();
} }
MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMax() { MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMax() {
nsIScrollableFrame* sf = GetScrollFrame(); nsIScrollableFrame* sf = GetScrollFrame();
return sf ? nsPresContext::AppUnitsToIntCSSPixels( if (!sf) {
sf->GetScrollRange().XMost()) return 0;
: 0; }
return CSSPixel::FromAppUnits(sf->GetScrollRange().XMost()).Rounded();
} }
MOZ_CAN_RUN_SCRIPT double ClientHeightDouble() { MOZ_CAN_RUN_SCRIPT double ClientHeightDouble() {
return nsPresContext::AppUnitsToDoubleCSSPixels( return CSSPixel::FromAppUnits(GetClientAreaRect().Height());
GetClientAreaRect().Height());
} }
MOZ_CAN_RUN_SCRIPT double ClientWidthDouble() { MOZ_CAN_RUN_SCRIPT double ClientWidthDouble() {
return nsPresContext::AppUnitsToDoubleCSSPixels( return CSSPixel::FromAppUnits(GetClientAreaRect().Width());
GetClientAreaRect().Width());
} }
// This function will return the block size of first line box, no matter if // 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, const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError); 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 MOZ_CAN_RUN_SCRIPT
void GetAnimations(const GetAnimationsOptions& aOptions, void GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations, nsTArray<RefPtr<Animation>>& aAnimations);
Flush aFlush = Flush::Yes);
void GetAnimationsWithoutFlush(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations);
static void GetAnimationsUnsorted(Element* aElement, static void GetAnimationsUnsorted(Element* aElement,
PseudoStyleType aPseudoType, PseudoStyleType aPseudoType,
@ -1588,8 +1568,10 @@ class Element : public FragmentOrElement {
*/ */
float FontSizeInflation(); float FontSizeInflation();
ReferrerPolicy GetReferrerPolicyAsEnum(); void GetImplementedPseudoElement(nsAString&) const;
ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue);
ReferrerPolicy GetReferrerPolicyAsEnum() const;
ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue) const;
/* /*
* Helpers for .dataset. This is implemented on Element, though only some * Helpers for .dataset. This is implemented on Element, though only some
@ -2039,6 +2021,11 @@ inline mozilla::dom::Element* nsINode::GetPreviousElementSibling() const {
return nullptr; 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 { inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
nsIContent* nextSibling = GetNextSibling(); nsIContent* nextSibling = GetNextSibling();
while (nextSibling) { while (nextSibling) {
@ -2060,7 +2047,8 @@ inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
nsINode** aResult) const { \ nsINode** aResult) const { \
*aResult = nullptr; \ *aResult = nullptr; \
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo); \ 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); \ nsresult rv = const_cast<_elementName*>(this)->CopyInnerTo(it); \
if (NS_SUCCEEDED(rv)) { \ if (NS_SUCCEEDED(rv)) { \
it.forget(aResult); \ it.forget(aResult); \
@ -2075,8 +2063,9 @@ inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
nsINode** aResult) const { \ nsINode** aResult) const { \
*aResult = nullptr; \ *aResult = nullptr; \
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo); \ RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo); \
auto* nim = ni->NodeInfoManager(); \
RefPtr<_elementName> it = \ RefPtr<_elementName> it = \
new _elementName(ni.forget() EXPAND extra_args_); \ new (nim) _elementName(ni.forget() EXPAND extra_args_); \
nsresult rv = it->Init(); \ nsresult rv = it->Init(); \
nsresult rv2 = const_cast<_elementName*>(this)->CopyInnerTo(it); \ nsresult rv2 = const_cast<_elementName*>(this)->CopyInnerTo(it); \
if (NS_FAILED(rv2)) { \ if (NS_FAILED(rv2)) { \

View File

@ -929,10 +929,9 @@ void EventSourceImpl::SetupHttpChannel() {
nsresult EventSourceImpl::SetupReferrerInfo() { nsresult EventSourceImpl::SetupReferrerInfo() {
AssertIsOnMainThread(); AssertIsOnMainThread();
MOZ_ASSERT(!IsShutDown()); MOZ_ASSERT(!IsShutDown());
nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent();
if (doc) { if (nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent()) {
nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(); auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
referrerInfo->InitWithDocument(doc);
nsresult rv = mHttpChannel->SetReferrerInfoWithoutClone(referrerInfo); nsresult rv = mHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
@ -1781,7 +1780,7 @@ EventSource::EventSource(nsIGlobalObject* aGlobal,
mImpl = new EventSourceImpl(this, aCookieSettings); mImpl = new EventSourceImpl(this, aCookieSettings);
} }
EventSource::~EventSource() {} EventSource::~EventSource() = default;
nsresult EventSource::CreateAndDispatchSimpleEvent(const nsAString& aName) { nsresult EventSource::CreateAndDispatchSimpleEvent(const nsAString& aName) {
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); 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; using namespace mozilla::dom;
FormData::FormData(nsISupports* aOwner, NotNull<const Encoding*> aEncoding, FormData::FormData(nsISupports* aOwner, NotNull<const Encoding*> aEncoding,
Element* aOriginatingElement) Element* aSubmitter)
: HTMLFormSubmission(nullptr, EmptyString(), aEncoding, : HTMLFormSubmission(nullptr, EmptyString(), aEncoding, aSubmitter),
aOriginatingElement),
mOwner(aOwner) {} mOwner(aOwner) {}
FormData::FormData(const FormData& aFormData) FormData::FormData(const FormData& aFormData)
: HTMLFormSubmission(aFormData.mActionURL, aFormData.mTarget, : HTMLFormSubmission(aFormData.mActionURL, aFormData.mTarget,
aFormData.mEncoding, aFormData.mOriginatingElement) { aFormData.mEncoding, aFormData.mSubmitter) {
mOwner = aFormData.mOwner; mOwner = aFormData.mOwner;
mFormData = aFormData.mFormData; mFormData = aFormData.mFormData;
} }

View File

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

View File

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

View File

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

View File

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

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