mirror of
https://github.com/Feodor2/Mypal68.git
synced 2025-06-18 14:55:44 -04:00
68.13.9 - dom
This commit is contained in:
parent
45b510c8d7
commit
40abca8e41
@ -17,6 +17,7 @@
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/DeclarationBlock.h"
|
||||
#include "mozilla/Maybe.h" // For Maybe
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/TypeTraits.h" // For std::forward<>
|
||||
#include "nsAnimationManager.h" // For CSSAnimation
|
||||
#include "nsComputedDOMStyle.h"
|
||||
@ -60,13 +61,13 @@ class MOZ_RAII AutoMutationBatchForAnimation {
|
||||
explicit AutoMutationBatchForAnimation(
|
||||
const Animation& aAnimation MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
Maybe<NonOwningAnimationTarget> target = aAnimation.GetTargetForAnimation();
|
||||
NonOwningAnimationTarget target = aAnimation.GetTargetForAnimation();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For mutation observers, we use the OwnerDoc.
|
||||
mAutoBatch.emplace(target->mElement->OwnerDoc());
|
||||
mAutoBatch.emplace(target.mElement->OwnerDoc());
|
||||
}
|
||||
|
||||
private:
|
||||
@ -81,12 +82,13 @@ class MOZ_RAII AutoMutationBatchForAnimation {
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Maybe<NonOwningAnimationTarget> Animation::GetTargetForAnimation() const {
|
||||
NonOwningAnimationTarget Animation::GetTargetForAnimation() const {
|
||||
AnimationEffect* effect = GetEffect();
|
||||
NonOwningAnimationTarget target;
|
||||
if (!effect || !effect->AsKeyframeEffect()) {
|
||||
return Nothing();
|
||||
return target;
|
||||
}
|
||||
return effect->AsKeyframeEffect()->GetTarget();
|
||||
return effect->AsKeyframeEffect()->GetAnimationTarget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -363,9 +365,15 @@ void Animation::UpdatePlaybackRate(double aPlaybackRate) {
|
||||
|
||||
mPendingPlaybackRate = Some(aPlaybackRate);
|
||||
|
||||
// If we already have a pending task, there is nothing more to do since the
|
||||
// playback rate will be applied then.
|
||||
if (Pending()) {
|
||||
// If we already have a pending task, there is nothing more to do since the
|
||||
// playback rate will be applied then.
|
||||
//
|
||||
// However, as with the idle/paused case below, we still need to update the
|
||||
// relevance (and effect set to make sure it only contains relevant
|
||||
// animations) since the relevance is based on the Animation play state
|
||||
// which incorporates the _pending_ playback rate.
|
||||
UpdateEffect(PostRestyleMode::Never);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -387,8 +395,9 @@ void Animation::UpdatePlaybackRate(double aPlaybackRate) {
|
||||
// moving. Once we get a start time etc. we'll update the playback rate
|
||||
// then.
|
||||
//
|
||||
// All we need to do is update observers so that, e.g. DevTools, report the
|
||||
// right information.
|
||||
// However we still need to update the relevance and effect set as well as
|
||||
// notifying observers.
|
||||
UpdateEffect(PostRestyleMode::Never);
|
||||
if (IsRelevant()) {
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
@ -639,18 +648,18 @@ void Animation::CommitStyles(ErrorResult& aRv) {
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target = keyframeEffect->GetTarget();
|
||||
NonOwningAnimationTarget target = keyframeEffect->GetAnimationTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->mPseudoType != PseudoStyleType::NotPseudo) {
|
||||
if (target.mPseudoType != PseudoStyleType::NotPseudo) {
|
||||
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check it is an element with a style attribute
|
||||
nsCOMPtr<nsStyledElement> styledElement = do_QueryInterface(target->mElement);
|
||||
nsCOMPtr<nsStyledElement> styledElement = do_QueryInterface(target.mElement);
|
||||
if (!styledElement) {
|
||||
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
|
||||
return;
|
||||
@ -658,16 +667,16 @@ void Animation::CommitStyles(ErrorResult& aRv) {
|
||||
|
||||
// Flush style before checking if the target element is rendered since the
|
||||
// result could depend on pending style changes.
|
||||
if (Document* doc = target->mElement->GetComposedDoc()) {
|
||||
if (Document* doc = target.mElement->GetComposedDoc()) {
|
||||
doc->FlushPendingNotifications(FlushType::Style);
|
||||
}
|
||||
if (!target->mElement->IsRendered()) {
|
||||
if (!target.mElement->IsRendered()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(target->mElement);
|
||||
nsContentUtils::GetContextForContent(target.mElement);
|
||||
if (!presContext) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -686,11 +695,11 @@ void Animation::CommitStyles(ErrorResult& aRv) {
|
||||
// Start the update now so that the old rule doesn't get used
|
||||
// between when we mutate the declaration and when we set the new
|
||||
// rule.
|
||||
mozAutoDocUpdate autoUpdate(target->mElement->OwnerDoc(), true);
|
||||
mozAutoDocUpdate autoUpdate(target.mElement->OwnerDoc(), true);
|
||||
|
||||
// Get the inline style to append to
|
||||
RefPtr<DeclarationBlock> declarationBlock;
|
||||
if (auto* existing = target->mElement->GetInlineStyleDeclaration()) {
|
||||
if (auto* existing = target.mElement->GetInlineStyleDeclaration()) {
|
||||
declarationBlock = existing->EnsureMutable();
|
||||
} else {
|
||||
declarationBlock = new DeclarationBlock();
|
||||
@ -700,7 +709,7 @@ void Animation::CommitStyles(ErrorResult& aRv) {
|
||||
// Prepare the callback
|
||||
MutationClosureData closureData;
|
||||
closureData.mClosure = nsDOMCSSAttributeDeclaration::MutationClosureFunction;
|
||||
closureData.mElement = target->mElement;
|
||||
closureData.mElement = target.mElement;
|
||||
DeclarationBlockMutationClosure beforeChangeClosure = {
|
||||
nsDOMCSSAttributeDeclaration::MutationClosureFunction,
|
||||
&closureData,
|
||||
@ -724,7 +733,7 @@ void Animation::CommitStyles(ErrorResult& aRv) {
|
||||
}
|
||||
|
||||
// Update inline style declaration
|
||||
target->mElement->SetInlineStyleDeclaration(*declarationBlock, closureData);
|
||||
target.mElement->SetInlineStyleDeclaration(*declarationBlock, closureData);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -749,7 +758,9 @@ void Animation::SetCurrentTimeAsDouble(const Nullable<double>& aCurrentTime,
|
||||
ErrorResult& aRv) {
|
||||
if (aCurrentTime.IsNull()) {
|
||||
if (!GetCurrentTimeAsDuration().IsNull()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
aRv.ThrowTypeError(
|
||||
"Current time is resolved but trying to set it to an unresolved "
|
||||
"time");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -963,7 +974,9 @@ bool Animation::ShouldBeSynchronizedWithMainThread(
|
||||
// We check this before calling ShouldBlockAsyncTransformAnimations, partly
|
||||
// because it's cheaper, but also because it's often the most useful thing
|
||||
// to know when you're debugging performance.
|
||||
if (mSyncWithGeometricAnimations &&
|
||||
if (StaticPrefs::
|
||||
dom_animations_mainthread_synchronization_with_geometric_animations() &&
|
||||
mSyncWithGeometricAnimations &&
|
||||
keyframeEffect->HasAnimationOfPropertySet(
|
||||
nsCSSPropertyIDSet::TransformLikeProperties())) {
|
||||
aPerformanceWarning =
|
||||
@ -1048,7 +1061,7 @@ bool Animation::IsReplaceable() const {
|
||||
// We should only replace animations with a target element (since otherwise
|
||||
// what other effects would we consider when determining if they are covered
|
||||
// or not?).
|
||||
if (!GetEffect()->AsKeyframeEffect()->GetTarget()) {
|
||||
if (!GetEffect()->AsKeyframeEffect()->GetAnimationTarget()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1067,15 +1080,16 @@ void Animation::ScheduleReplacementCheck() {
|
||||
// If IsReplaceable() is true, the following should also hold
|
||||
MOZ_ASSERT(GetEffect());
|
||||
MOZ_ASSERT(GetEffect()->AsKeyframeEffect());
|
||||
MOZ_ASSERT(GetEffect()->AsKeyframeEffect()->GetTarget());
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target =
|
||||
GetEffect()->AsKeyframeEffect()->GetTarget();
|
||||
NonOwningAnimationTarget target =
|
||||
GetEffect()->AsKeyframeEffect()->GetAnimationTarget();
|
||||
|
||||
MOZ_ASSERT(target);
|
||||
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(target->mElement);
|
||||
nsContentUtils::GetContextForContent(target.mElement);
|
||||
if (presContext) {
|
||||
presContext->EffectCompositor()->NoteElementForReducing(*target);
|
||||
presContext->EffectCompositor()->NoteElementForReducing(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ class Document;
|
||||
class Animation : public DOMEventTargetHelper,
|
||||
public LinkedListElement<Animation> {
|
||||
protected:
|
||||
virtual ~Animation() {}
|
||||
virtual ~Animation() = default;
|
||||
|
||||
public:
|
||||
explicit Animation(nsIGlobalObject* aGlobal)
|
||||
@ -61,7 +61,7 @@ class Animation : public DOMEventTargetHelper,
|
||||
* Utility function to get the target (pseudo-)element associated with an
|
||||
* animation.
|
||||
*/
|
||||
Maybe<NonOwningAnimationTarget> GetTargetForAnimation() const;
|
||||
NonOwningAnimationTarget GetTargetForAnimation() const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
@ -88,7 +88,7 @@ class Animation : public DOMEventTargetHelper,
|
||||
void SetId(const nsAString& aId);
|
||||
|
||||
AnimationEffect* GetEffect() const { return mEffect; }
|
||||
void SetEffect(AnimationEffect* aEffect);
|
||||
virtual void SetEffect(AnimationEffect* aEffect);
|
||||
void SetEffectNoUpdate(AnimationEffect* aEffect);
|
||||
|
||||
AnimationTimeline* GetTimeline() const { return mTimeline; }
|
||||
@ -98,7 +98,7 @@ class Animation : public DOMEventTargetHelper,
|
||||
Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
|
||||
Nullable<double> GetStartTimeAsDouble() const;
|
||||
void SetStartTime(const Nullable<TimeDuration>& aNewStartTime);
|
||||
void SetStartTimeAsDouble(const Nullable<double>& aStartTime);
|
||||
virtual void SetStartTimeAsDouble(const Nullable<double>& aStartTime);
|
||||
|
||||
// This is deliberately _not_ called GetCurrentTime since that would clash
|
||||
// with a macro defined in winbase.h
|
||||
@ -131,21 +131,16 @@ class Animation : public DOMEventTargetHelper,
|
||||
|
||||
void Finish(ErrorResult& aRv);
|
||||
|
||||
virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior);
|
||||
void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior);
|
||||
virtual void PlayFromJS(ErrorResult& aRv) {
|
||||
Play(aRv, LimitBehavior::AutoRewind);
|
||||
}
|
||||
|
||||
virtual void Pause(ErrorResult& aRv);
|
||||
/**
|
||||
* PauseFromJS is currently only here for symmetry with PlayFromJS but
|
||||
* in future we will likely have to flush style in
|
||||
* CSSAnimation::PauseFromJS so we leave it for now.
|
||||
*/
|
||||
void PauseFromJS(ErrorResult& aRv) { Pause(aRv); }
|
||||
void Pause(ErrorResult& aRv);
|
||||
virtual void PauseFromJS(ErrorResult& aRv) { Pause(aRv); }
|
||||
|
||||
void UpdatePlaybackRate(double aPlaybackRate);
|
||||
void Reverse(ErrorResult& aRv);
|
||||
virtual void Reverse(ErrorResult& aRv);
|
||||
|
||||
void Persist();
|
||||
void CommitStyles(ErrorResult& aRv);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -71,8 +72,8 @@ void AnimationEffect::SetSpecifiedTiming(TimingParams&& aTiming) {
|
||||
|
||||
if (mAnimation) {
|
||||
Maybe<nsAutoAnimationMutationBatch> mb;
|
||||
if (AsKeyframeEffect() && AsKeyframeEffect()->GetTarget()) {
|
||||
mb.emplace(AsKeyframeEffect()->GetTarget()->mElement->OwnerDoc());
|
||||
if (AsKeyframeEffect() && AsKeyframeEffect()->GetAnimationTarget()) {
|
||||
mb.emplace(AsKeyframeEffect()->GetAnimationTarget().mElement->OwnerDoc());
|
||||
}
|
||||
|
||||
mAnimation->NotifyEffectTimingUpdated();
|
||||
|
@ -16,9 +16,6 @@
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct ElementPropertyTransition;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Animation;
|
||||
@ -35,11 +32,6 @@ class AnimationEffect : public nsISupports, public nsWrapperCache {
|
||||
|
||||
virtual KeyframeEffect* AsKeyframeEffect() { return nullptr; }
|
||||
|
||||
virtual ElementPropertyTransition* AsTransition() { return nullptr; }
|
||||
virtual const ElementPropertyTransition* AsTransition() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const { return ToSupports(mDocument); }
|
||||
|
||||
bool IsCurrent() const;
|
||||
@ -49,9 +41,10 @@ class AnimationEffect : public nsISupports, public nsWrapperCache {
|
||||
}
|
||||
|
||||
// AnimationEffect interface
|
||||
void GetTiming(EffectTiming& aRetVal) const;
|
||||
void GetComputedTimingAsDict(ComputedEffectTiming& aRetVal) const;
|
||||
void UpdateTiming(const OptionalEffectTiming& aTiming, ErrorResult& aRv);
|
||||
virtual void GetTiming(EffectTiming& aRetVal) const;
|
||||
virtual void GetComputedTimingAsDict(ComputedEffectTiming& aRetVal) const;
|
||||
virtual void UpdateTiming(const OptionalEffectTiming& aTiming,
|
||||
ErrorResult& aRv);
|
||||
|
||||
const TimingParams& SpecifiedTiming() const { return mTiming; }
|
||||
void SetSpecifiedTiming(TimingParams&& aTiming);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "mozilla/AnimationEventDispatcher.h"
|
||||
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -66,6 +66,9 @@ bool AnimationPerformanceWarning::ToLocalizedString(
|
||||
case Type::HasRenderingObserver:
|
||||
key = "CompositorAnimationWarningHasRenderingObserver";
|
||||
break;
|
||||
case Type::HasCurrentColor:
|
||||
key = "CompositorAnimationWarningHasCurrentColor";
|
||||
break;
|
||||
case Type::None:
|
||||
MOZ_ASSERT_UNREACHABLE("Uninitialized type shouldn't be used");
|
||||
return false;
|
||||
|
@ -28,6 +28,7 @@ struct AnimationPerformanceWarning {
|
||||
TransformIsBlockedByImportantRules,
|
||||
OpacityFrameInactive,
|
||||
HasRenderingObserver,
|
||||
HasCurrentColor,
|
||||
};
|
||||
|
||||
explicit AnimationPerformanceWarning(Type aType) : mType(aType) {
|
||||
|
@ -19,6 +19,8 @@ class Element;
|
||||
} // namespace dom
|
||||
|
||||
struct OwningAnimationTarget {
|
||||
OwningAnimationTarget() = default;
|
||||
|
||||
OwningAnimationTarget(dom::Element* aElement, PseudoStyleType aType)
|
||||
: mElement(aElement), mPseudoType(aType) {}
|
||||
|
||||
@ -28,6 +30,8 @@ struct OwningAnimationTarget {
|
||||
return mElement == aOther.mElement && mPseudoType == aOther.mPseudoType;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return !!mElement; }
|
||||
|
||||
// mElement represents the parent element of a pseudo-element, not the
|
||||
// generated content element.
|
||||
RefPtr<dom::Element> mElement;
|
||||
@ -47,6 +51,14 @@ struct NonOwningAnimationTarget {
|
||||
return mElement == aOther.mElement && mPseudoType == aOther.mPseudoType;
|
||||
}
|
||||
|
||||
NonOwningAnimationTarget& operator=(const OwningAnimationTarget& aOther) {
|
||||
mElement = aOther.mElement;
|
||||
mPseudoType = aOther.mPseudoType;
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return !!mElement; }
|
||||
|
||||
// mElement represents the parent element of a pseudo-element, not the
|
||||
// generated content element.
|
||||
dom::Element* MOZ_NON_OWNING_REF mElement = nullptr;
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
#include "AnimationUtils.h"
|
||||
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/KeyframeEffect.h"
|
||||
#include "mozilla/EffectSet.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsAtom.h"
|
||||
#include "nsIContent.h"
|
||||
@ -56,20 +56,6 @@ Document* AnimationUtils::GetDocumentFromGlobal(JSObject* aGlobalObject) {
|
||||
return win->GetDoc();
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool AnimationUtils::IsOffscreenThrottlingEnabled() {
|
||||
static bool sOffscreenThrottlingEnabled;
|
||||
static bool sPrefCached = false;
|
||||
|
||||
if (!sPrefCached) {
|
||||
sPrefCached = true;
|
||||
Preferences::AddBoolVarCache(&sOffscreenThrottlingEnabled,
|
||||
"dom.animations.offscreen-throttling");
|
||||
}
|
||||
|
||||
return sOffscreenThrottlingEnabled;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool AnimationUtils::FrameHasAnimatedScale(const nsIFrame* aFrame) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSetForFrame(
|
||||
|
@ -71,11 +71,6 @@ class AnimationUtils {
|
||||
*/
|
||||
static Document* GetDocumentFromGlobal(JSObject* aGlobalObject);
|
||||
|
||||
/**
|
||||
* Checks if offscreen animation throttling is enabled.
|
||||
*/
|
||||
static bool IsOffscreenThrottlingEnabled();
|
||||
|
||||
/**
|
||||
* Returns true if the given frame has an animated scale.
|
||||
*/
|
||||
|
365
dom/animation/CSSAnimation.cpp
Normal file
365
dom/animation/CSSAnimation.cpp
Normal 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
|
247
dom/animation/CSSAnimation.h
Normal file
247
dom/animation/CSSAnimation.h
Normal 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
|
@ -29,7 +29,7 @@ CSSPseudoElement::CSSPseudoElement(dom::Element* aElement,
|
||||
CSSPseudoElement::~CSSPseudoElement() {
|
||||
// Element might have been unlinked already, so we have to do null check.
|
||||
if (mOriginatingElement) {
|
||||
mOriginatingElement->DeleteProperty(
|
||||
mOriginatingElement->RemoveProperty(
|
||||
GetCSSPseudoElementPropertyAtom(mPseudoType));
|
||||
}
|
||||
}
|
||||
@ -43,31 +43,6 @@ JSObject* CSSPseudoElement::WrapObject(JSContext* aCx,
|
||||
return CSSPseudoElement_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void CSSPseudoElement::GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aRetVal) {
|
||||
Document* doc = mOriginatingElement->GetComposedDoc();
|
||||
if (doc) {
|
||||
// We don't need to explicitly flush throttled animations here, since
|
||||
// updating the animation style of (pseudo-)elements will never affect the
|
||||
// set of running animations and it's only the set of running animations
|
||||
// that is important here.
|
||||
doc->FlushPendingNotifications(
|
||||
ChangesToFlush(FlushType::Style, false /* flush animations */));
|
||||
}
|
||||
|
||||
Element::GetAnimationsUnsorted(mOriginatingElement, mPseudoType, aRetVal);
|
||||
aRetVal.Sort(AnimationPtrComparator<RefPtr<Animation>>());
|
||||
}
|
||||
|
||||
already_AddRefed<Animation> CSSPseudoElement::Animate(
|
||||
JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError) {
|
||||
Nullable<ElementOrCSSPseudoElement> target;
|
||||
target.SetValue().SetAsCSSPseudoElement() = this;
|
||||
return Element::Animate(target, aContext, aKeyframes, aOptions, aError);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<CSSPseudoElement> CSSPseudoElement::GetCSSPseudoElement(
|
||||
dom::Element* aElement, PseudoStyleType aType) {
|
||||
|
@ -52,13 +52,6 @@ class CSSPseudoElement final : public nsWrapperCache {
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
void GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aRetVal);
|
||||
already_AddRefed<Animation> Animate(
|
||||
JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError);
|
||||
|
||||
// Given an element:pseudoType pair, returns the CSSPseudoElement stored as a
|
||||
// property on |aElement|. If there is no CSSPseudoElement for the specified
|
||||
// pseudo-type on element, a new CSSPseudoElement will be created and stored
|
||||
|
336
dom/animation/CSSTransition.cpp
Normal file
336
dom/animation/CSSTransition.cpp
Normal 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
|
243
dom/animation/CSSTransition.h
Normal file
243
dom/animation/CSSTransition.h
Normal 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
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "DocumentTimeline.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/DocumentTimelineBinding.h"
|
||||
#include "AnimationUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
@ -58,8 +59,7 @@ already_AddRefed<DocumentTimeline> DocumentTimeline::Constructor(
|
||||
|
||||
if (originTime == TimeDuration::Forever() ||
|
||||
originTime == -TimeDuration::Forever()) {
|
||||
aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>(
|
||||
NS_LITERAL_STRING("Origin time"));
|
||||
aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>("Origin time");
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<DocumentTimeline> timeline = new DocumentTimeline(doc, originTime);
|
||||
|
@ -269,6 +269,12 @@ void EffectCompositor::PostRestyleForAnimation(dom::Element* aElement,
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Bug 1615083 KeyframeEffect::SetTarget() and
|
||||
// KeyframeEffect::SetPseudoElement() may set a non-existing pseudo element,
|
||||
// and we still have to update its style, based on the wpt. However, we don't
|
||||
// have the generated element here, so we failed the wpt.
|
||||
//
|
||||
// See wpt for more info: web-animations/interfaces/KeyframeEffect/target.html
|
||||
dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
|
||||
if (!element) {
|
||||
return;
|
||||
@ -389,19 +395,31 @@ static void ComposeSortedEffects(
|
||||
const nsTArray<KeyframeEffect*>& aSortedEffects,
|
||||
const EffectSet* aEffectSet, EffectCompositor::CascadeLevel aCascadeLevel,
|
||||
RawServoAnimationValueMap* aAnimationValues) {
|
||||
// If multiple animations affect the same property, animations with higher
|
||||
// composite order (priority) override or add to animations with lower
|
||||
// priority.
|
||||
const bool isTransition =
|
||||
aCascadeLevel == EffectCompositor::CascadeLevel::Transitions;
|
||||
nsCSSPropertyIDSet propertiesToSkip;
|
||||
// Transitions should be overridden by running animations of the same
|
||||
// property per https://drafts.csswg.org/css-transitions/#application:
|
||||
//
|
||||
// > Implementations must add this value to the cascade if and only if that
|
||||
// > property is not currently undergoing a CSS Animation on the same element.
|
||||
//
|
||||
// FIXME(emilio, bug 1606176): This should assert that
|
||||
// aEffectSet->PropertiesForAnimationsLevel() is up-to-date, and it may not
|
||||
// follow the spec in those cases. There are various places where we get style
|
||||
// without flushing that would trigger the below assertion.
|
||||
//
|
||||
// MOZ_ASSERT_IF(aEffectSet, !aEffectSet->CascadeNeedsUpdate());
|
||||
if (aEffectSet) {
|
||||
propertiesToSkip =
|
||||
aCascadeLevel == EffectCompositor::CascadeLevel::Animations
|
||||
? aEffectSet->PropertiesForAnimationsLevel().Inverse()
|
||||
: aEffectSet->PropertiesForAnimationsLevel();
|
||||
isTransition ? aEffectSet->PropertiesForAnimationsLevel()
|
||||
: aEffectSet->PropertiesForAnimationsLevel().Inverse();
|
||||
}
|
||||
|
||||
for (KeyframeEffect* effect : aSortedEffects) {
|
||||
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
|
||||
auto* animation = effect->GetAnimation();
|
||||
MOZ_ASSERT(!isTransition || animation->CascadeLevel() == aCascadeLevel);
|
||||
animation->ComposeStyle(*aAnimationValues, propertiesToSkip);
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,11 +439,27 @@ bool EffectCompositor::GetServoAnimationRule(
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool isTransition = aCascadeLevel == CascadeLevel::Transitions;
|
||||
|
||||
// Get a list of effects sorted by composite order.
|
||||
nsTArray<KeyframeEffect*> sortedEffectList(effectSet->Count());
|
||||
for (KeyframeEffect* effect : *effectSet) {
|
||||
if (isTransition &&
|
||||
effect->GetAnimation()->CascadeLevel() != aCascadeLevel) {
|
||||
// We may need to use transition rules for the animations level for the
|
||||
// case of missing keyframes in animations, but we don't ever need to look
|
||||
// at non-transition levels to build a transition rule. When the effect
|
||||
// set information is out of date (see above), this avoids creating bogus
|
||||
// transition rules, see bug 1605610.
|
||||
continue;
|
||||
}
|
||||
sortedEffectList.AppendElement(effect);
|
||||
}
|
||||
|
||||
if (sortedEffectList.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
||||
|
||||
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
|
||||
@ -444,14 +478,14 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
|
||||
MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(),
|
||||
"Should not be in print preview");
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target = aEffect.GetTarget();
|
||||
NonOwningAnimationTarget target = aEffect.GetAnimationTarget();
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't try to compose animations for elements in documents without a pres
|
||||
// shell (e.g. XMLHttpRequest documents).
|
||||
if (!nsContentUtils::GetPresShellForContent(target->mElement)) {
|
||||
if (!nsContentUtils::GetPresShellForContent(target.mElement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -459,11 +493,10 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
|
||||
// where the cascade results are updated in the pre-traversal as needed.
|
||||
// This function, however, is only called when committing styles so we
|
||||
// need to ensure the cascade results are up-to-date manually.
|
||||
EffectCompositor::MaybeUpdateCascadeResults(target->mElement,
|
||||
target->mPseudoType);
|
||||
MaybeUpdateCascadeResults(target.mElement, target.mPseudoType);
|
||||
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(target->mElement, target->mPseudoType);
|
||||
EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
|
||||
|
||||
// Get a list of effects sorted by composite order up to and including
|
||||
// |aEffect|, even if it is not in the EffectSet.
|
||||
@ -483,9 +516,9 @@ bool EffectCompositor::ComposeServoAnimationRuleForEffect(
|
||||
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
|
||||
aAnimationValues);
|
||||
|
||||
MOZ_ASSERT(effectSet ==
|
||||
EffectSet::GetEffectSet(target->mElement, target->mPseudoType),
|
||||
"EffectSet should not change while composing style");
|
||||
MOZ_ASSERT(
|
||||
effectSet == EffectSet::GetEffectSet(target.mElement, target.mPseudoType),
|
||||
"EffectSet should not change while composing style");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "EffectSet.h"
|
||||
#include "mozilla/dom/Element.h" // For Element
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "mozilla/LayerAnimationInfo.h"
|
||||
#include "nsCSSPseudoElements.h" // For PseudoStyleType
|
||||
#include "nsCycleCollectionNoteChild.h" // For CycleCollectionNoteChild
|
||||
#include "nsPresContext.h"
|
||||
@ -135,7 +136,7 @@ void EffectSet::DestroyEffectSet(dom::Element* aElement,
|
||||
"Should not destroy an effect set while it is being enumerated");
|
||||
effectSet = nullptr;
|
||||
|
||||
aElement->DeleteProperty(propName);
|
||||
aElement->RemoveProperty(propName);
|
||||
}
|
||||
|
||||
void EffectSet::UpdateAnimationGeneration(nsPresContext* aPresContext) {
|
||||
|
@ -61,17 +61,10 @@ struct PropertyValuePair {
|
||||
struct Keyframe {
|
||||
Keyframe() = default;
|
||||
Keyframe(const Keyframe& aOther) = default;
|
||||
Keyframe(Keyframe&& aOther) { *this = std::move(aOther); }
|
||||
Keyframe(Keyframe&& aOther) = default;
|
||||
|
||||
Keyframe& operator=(const Keyframe& aOther) = default;
|
||||
Keyframe& operator=(Keyframe&& aOther) {
|
||||
mOffset = aOther.mOffset;
|
||||
mComputedOffset = aOther.mComputedOffset;
|
||||
mTimingFunction = std::move(aOther.mTimingFunction);
|
||||
mComposite = std::move(aOther.mComposite);
|
||||
mPropertyValues = std::move(aOther.mPropertyValues);
|
||||
return *this;
|
||||
}
|
||||
Keyframe& operator=(Keyframe&& aOther) = default;
|
||||
|
||||
Maybe<double> mOffset;
|
||||
static constexpr double kComputedOffsetNotSet = -1.0;
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
|
||||
// For UnrestrictedDoubleOrKeyframeAnimationOptions;
|
||||
#include "mozilla/dom/CSSPseudoElement.h"
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h"
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
@ -34,13 +33,14 @@
|
||||
#include "nsCSSPseudoElements.h" // For PseudoStyleType
|
||||
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIFrameInlines.h"
|
||||
#include "nsPresContextInlines.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void AnimationProperty::SetPerformanceWarning(
|
||||
const AnimationPerformanceWarning& aWarning, const Element* aElement) {
|
||||
const AnimationPerformanceWarning& aWarning, const dom::Element* aElement) {
|
||||
if (mPerformanceWarning && *mPerformanceWarning == aWarning) {
|
||||
return;
|
||||
}
|
||||
@ -71,7 +71,8 @@ bool PropertyValuePair::operator==(const PropertyValuePair& aOther) const {
|
||||
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffect, AnimationEffect, mTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffect, AnimationEffect,
|
||||
mTarget.mElement)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffect, AnimationEffect)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
@ -83,11 +84,11 @@ NS_IMPL_ADDREF_INHERITED(KeyframeEffect, AnimationEffect)
|
||||
NS_IMPL_RELEASE_INHERITED(KeyframeEffect, AnimationEffect)
|
||||
|
||||
KeyframeEffect::KeyframeEffect(Document* aDocument,
|
||||
const Maybe<OwningAnimationTarget>& aTarget,
|
||||
OwningAnimationTarget&& aTarget,
|
||||
TimingParams&& aTiming,
|
||||
const KeyframeEffectParams& aOptions)
|
||||
: AnimationEffect(aDocument, std::move(aTiming)),
|
||||
mTarget(aTarget),
|
||||
mTarget(std::move(aTarget)),
|
||||
mEffectOptions(aOptions),
|
||||
mCumulativeChangeHint(nsChangeHint(0)) {}
|
||||
|
||||
@ -140,7 +141,7 @@ void KeyframeEffect::SetComposite(const CompositeOperation& aComposite) {
|
||||
void KeyframeEffect::NotifySpecifiedTimingUpdated() {
|
||||
// Use the same document for a pseudo element and its parent element.
|
||||
// Use nullptr if we don't have mTarget, so disable the mutation batch.
|
||||
nsAutoAnimationMutationBatch mb(mTarget ? mTarget->mElement->OwnerDoc()
|
||||
nsAutoAnimationMutationBatch mb(mTarget ? mTarget.mElement->OwnerDoc()
|
||||
: nullptr);
|
||||
|
||||
if (mAnimation) {
|
||||
@ -221,7 +222,7 @@ void KeyframeEffect::SetKeyframes(JSContext* aContext,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
ErrorResult& aRv) {
|
||||
nsTArray<Keyframe> keyframes = KeyframeUtils::GetKeyframesFromObject(
|
||||
aContext, mDocument, aKeyframes, "KeyframeEffect.setKeyframes: ", aRv);
|
||||
aContext, mDocument, aKeyframes, "KeyframeEffect.setKeyframes", aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
@ -250,6 +251,36 @@ void KeyframeEffect::SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeEffect::ReplaceTransitionStartValue(AnimationValue&& aStartValue) {
|
||||
if (!aStartValue.mServo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A typical transition should have a single property and a single segment.
|
||||
//
|
||||
// (And for atypical transitions, that is, those updated by script, we don't
|
||||
// apply the replacing behavior.)
|
||||
if (mProperties.Length() != 1 || mProperties[0].mSegments.Length() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Likewise, check that the keyframes are of the expected shape.
|
||||
if (mKeyframes.Length() != 2 || mKeyframes[0].mPropertyValues.Length() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that the value we are about to substitute in is actually for the
|
||||
// same property.
|
||||
if (Servo_AnimationValue_GetPropertyId(aStartValue.mServo) !=
|
||||
mProperties[0].mProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
mKeyframes[0].mPropertyValues[0].mServoDeclarationBlock =
|
||||
Servo_AnimationValue_Uncompute(aStartValue.mServo).Consume();
|
||||
mProperties[0].mSegments[0].mFromValue = std::move(aStartValue);
|
||||
}
|
||||
|
||||
static bool IsEffectiveProperty(const EffectSet& aEffects,
|
||||
nsCSSPropertyID aProperty) {
|
||||
return !aEffects.PropertiesWithImportantRules().HasProperty(aProperty) ||
|
||||
@ -258,8 +289,9 @@ static bool IsEffectiveProperty(const EffectSet& aEffects,
|
||||
|
||||
const AnimationProperty* KeyframeEffect::GetEffectiveAnimationOfProperty(
|
||||
nsCSSPropertyID aProperty, const EffectSet& aEffects) const {
|
||||
MOZ_ASSERT(&aEffects ==
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType));
|
||||
MOZ_ASSERT(mTarget &&
|
||||
&aEffects == EffectSet::GetEffectSet(mTarget.mElement,
|
||||
mTarget.mPseudoType));
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
if (aProperty != property.mProperty) {
|
||||
@ -291,7 +323,7 @@ bool KeyframeEffect::HasEffectiveAnimationOfPropertySet(
|
||||
nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
|
||||
EffectSet& aEffects, const nsIFrame* aFrame) const {
|
||||
MOZ_ASSERT(&aEffects ==
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType));
|
||||
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType));
|
||||
|
||||
nsCSSPropertyIDSet properties;
|
||||
|
||||
@ -372,6 +404,16 @@ bool SpecifiedKeyframeArraysAreEqual(const nsTArray<Keyframe>& aA,
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool HasCurrentColor(
|
||||
const nsTArray<AnimationPropertySegment>& aSegments) {
|
||||
for (const AnimationPropertySegment& segment : aSegments) {
|
||||
if ((!segment.mFromValue.IsNull() && segment.mFromValue.IsCurrentColor()) ||
|
||||
(!segment.mToValue.IsNull() && segment.mToValue.IsCurrentColor())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
|
||||
MOZ_ASSERT(aStyle);
|
||||
|
||||
@ -391,7 +433,7 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
|
||||
}
|
||||
// Check if we need to update the cumulative change hint because we now have
|
||||
// style data.
|
||||
if (mNeedsStyleData && mTarget && mTarget->mElement->HasServoData()) {
|
||||
if (mNeedsStyleData && mTarget && mTarget.mElement->HasServoData()) {
|
||||
CalculateCumulativeChangeHint(aStyle);
|
||||
}
|
||||
return;
|
||||
@ -409,9 +451,19 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
|
||||
mProperties = std::move(properties);
|
||||
UpdateEffectSet();
|
||||
|
||||
mHasCurrentColor = false;
|
||||
|
||||
for (AnimationProperty& property : mProperties) {
|
||||
property.mIsRunningOnCompositor =
|
||||
runningOnCompositorProperties.HasProperty(property.mProperty);
|
||||
|
||||
if (property.mProperty == eCSSProperty_background_color &&
|
||||
!mHasCurrentColor) {
|
||||
if (HasCurrentColor(property.mSegments)) {
|
||||
mHasCurrentColor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CalculateCumulativeChangeHint(aStyle);
|
||||
@ -444,7 +496,7 @@ void KeyframeEffect::EnsureBaseStyles(
|
||||
mBaseValues.Clear();
|
||||
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(mTarget->mElement);
|
||||
nsContentUtils::GetContextForContent(mTarget.mElement);
|
||||
// If |aProperties| is empty we're not going to dereference |presContext| so
|
||||
// we don't care if it is nullptr.
|
||||
//
|
||||
@ -493,8 +545,10 @@ void KeyframeEffect::EnsureBaseStyle(
|
||||
}
|
||||
|
||||
if (!aBaseComputedStyle) {
|
||||
MOZ_ASSERT(mTarget, "Should have a valid target");
|
||||
|
||||
Element* animatingElement = EffectCompositor::GetElementToRestyle(
|
||||
mTarget->mElement, mTarget->mPseudoType);
|
||||
mTarget.mElement, mTarget.mPseudoType);
|
||||
if (!animatingElement) {
|
||||
return;
|
||||
}
|
||||
@ -505,7 +559,7 @@ void KeyframeEffect::EnsureBaseStyle(
|
||||
Servo_ComputedValues_ExtractAnimationValue(aBaseComputedStyle,
|
||||
aProperty.mProperty)
|
||||
.Consume();
|
||||
mBaseValues.Put(aProperty.mProperty, baseValue);
|
||||
mBaseValues.Put(aProperty.mProperty, std::move(baseValue));
|
||||
}
|
||||
|
||||
void KeyframeEffect::WillComposeStyle() {
|
||||
@ -578,9 +632,9 @@ void KeyframeEffect::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
|
||||
// out of view.
|
||||
if (HasPropertiesThatMightAffectOverflow()) {
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(mTarget->mElement);
|
||||
nsContentUtils::GetContextForContent(mTarget.mElement);
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
if (presContext && effectSet) {
|
||||
TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
|
||||
effectSet->UpdateLastOverflowAnimationSyncTime(now);
|
||||
@ -646,6 +700,12 @@ void KeyframeEffect::ResetIsRunningOnCompositor() {
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSupportedPseudoForWebAnimation(PseudoStyleType aType) {
|
||||
// FIXME: Bug 1615469: Support first-line and first-letter for Web Animation.
|
||||
return aType == PseudoStyleType::before || aType == PseudoStyleType::after ||
|
||||
aType == PseudoStyleType::marker;
|
||||
}
|
||||
|
||||
static const KeyframeEffectOptions& KeyframeEffectOptionsFromUnion(
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions) {
|
||||
MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
|
||||
@ -660,50 +720,52 @@ static const KeyframeEffectOptions& KeyframeEffectOptionsFromUnion(
|
||||
|
||||
template <class OptionsType>
|
||||
static KeyframeEffectParams KeyframeEffectParamsFromUnion(
|
||||
const OptionsType& aOptions, CallerType aCallerType) {
|
||||
const OptionsType& aOptions, CallerType aCallerType, ErrorResult& aRv) {
|
||||
KeyframeEffectParams result;
|
||||
if (aOptions.IsUnrestrictedDouble() ||
|
||||
// Ignore iterationComposite and composite if the corresponding pref is
|
||||
// not set. The default value 'Replace' will be used instead.
|
||||
!StaticPrefs::dom_animations_api_compositing_enabled()) {
|
||||
if (aOptions.IsUnrestrictedDouble()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const KeyframeEffectOptions& options =
|
||||
KeyframeEffectOptionsFromUnion(aOptions);
|
||||
|
||||
// If dom.animations-api.compositing.enabled is turned off,
|
||||
// iterationComposite and composite are the default value 'replace' in the
|
||||
// dictionary.
|
||||
result.mIterationComposite = options.mIterationComposite;
|
||||
result.mComposite = options.mComposite;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */
|
||||
Maybe<OwningAnimationTarget> KeyframeEffect::ConvertTarget(
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget) {
|
||||
// Return value optimization.
|
||||
Maybe<OwningAnimationTarget> result;
|
||||
|
||||
if (aTarget.IsNull()) {
|
||||
result.mPseudoType = PseudoStyleType::NotPseudo;
|
||||
if (DOMStringIsNull(options.mPseudoElement)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const ElementOrCSSPseudoElement& target = aTarget.Value();
|
||||
MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
|
||||
"Uninitialized target");
|
||||
|
||||
if (target.IsElement()) {
|
||||
result.emplace(&target.GetAsElement());
|
||||
} else {
|
||||
RefPtr<Element> elem = target.GetAsCSSPseudoElement().Element();
|
||||
result.emplace(elem, target.GetAsCSSPseudoElement().GetType());
|
||||
RefPtr<nsAtom> pseudoAtom =
|
||||
nsCSSPseudoElements::GetPseudoAtom(options.mPseudoElement);
|
||||
if (!pseudoAtom) {
|
||||
// Per the spec, we throw SyntaxError for syntactically invalid pseudos.
|
||||
aRv.ThrowSyntaxError(
|
||||
nsPrintfCString("'%s' is a syntactically invalid pseudo-element.",
|
||||
NS_ConvertUTF16toUTF8(options.mPseudoElement).get()));
|
||||
return result;
|
||||
}
|
||||
|
||||
result.mPseudoType = nsCSSPseudoElements::GetPseudoType(
|
||||
pseudoAtom, CSSEnabledState::ForAllContent);
|
||||
if (!IsSupportedPseudoForWebAnimation(result.mPseudoType)) {
|
||||
// Per the spec, we throw SyntaxError for unsupported pseudos.
|
||||
aRv.ThrowSyntaxError(
|
||||
nsPrintfCString("'%s' is an unsupported pseudo-element.",
|
||||
NS_ConvertUTF16toUTF8(options.mPseudoElement).get()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class OptionsType>
|
||||
/* static */
|
||||
already_AddRefed<KeyframeEffect> KeyframeEffect::ConstructKeyframeEffect(
|
||||
const GlobalObject& aGlobal,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
const GlobalObject& aGlobal, Element* aTarget,
|
||||
JS::Handle<JSObject*> aKeyframes, const OptionsType& aOptions,
|
||||
ErrorResult& aRv) {
|
||||
// We should get the document from `aGlobal` instead of the current Realm
|
||||
@ -721,18 +783,22 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::ConstructKeyframeEffect(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KeyframeEffectParams effectOptions =
|
||||
KeyframeEffectParamsFromUnion(aOptions, aGlobal.CallerType(), aRv);
|
||||
// An invalid Pseudo-element aborts all further steps.
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TimingParams timingParams =
|
||||
TimingParams::FromOptionsUnion(aOptions, doc, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KeyframeEffectParams effectOptions =
|
||||
KeyframeEffectParamsFromUnion(aOptions, aGlobal.CallerType());
|
||||
|
||||
Maybe<OwningAnimationTarget> target = ConvertTarget(aTarget);
|
||||
RefPtr<KeyframeEffect> effect =
|
||||
new KeyframeEffect(doc, target, std::move(timingParams), effectOptions);
|
||||
RefPtr<KeyframeEffect> effect = new KeyframeEffect(
|
||||
doc, OwningAnimationTarget(aTarget, effectOptions.mPseudoType),
|
||||
std::move(timingParams), effectOptions);
|
||||
|
||||
effect->SetKeyframes(aGlobal.Context(), aKeyframes, aRv);
|
||||
if (aRv.Failed()) {
|
||||
@ -747,7 +813,8 @@ nsTArray<AnimationProperty> KeyframeEffect::BuildProperties(
|
||||
MOZ_ASSERT(aStyle);
|
||||
|
||||
nsTArray<AnimationProperty> result;
|
||||
// If mTarget is null, return an empty property array.
|
||||
// If mTarget is false (i.e. mTarget.mElement is null), return an empty
|
||||
// property array.
|
||||
if (!mTarget) {
|
||||
return result;
|
||||
}
|
||||
@ -761,7 +828,7 @@ nsTArray<AnimationProperty> KeyframeEffect::BuildProperties(
|
||||
auto keyframesCopy(mKeyframes);
|
||||
|
||||
result = KeyframeUtils::GetAnimationPropertiesFromKeyframes(
|
||||
keyframesCopy, mTarget->mElement, aStyle, mEffectOptions.mComposite);
|
||||
keyframesCopy, mTarget.mElement, aStyle, mEffectOptions.mComposite);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
|
||||
@ -782,6 +849,50 @@ static void EnumerateContinuationsOrIBSplitSiblings(nsIFrame* aFrame,
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeEffect::UpdateTarget(Element* aElement,
|
||||
PseudoStyleType aPseudoType) {
|
||||
OwningAnimationTarget newTarget(aElement, aPseudoType);
|
||||
|
||||
if (mTarget == newTarget) {
|
||||
// Assign the same target, skip it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTarget) {
|
||||
UnregisterTarget();
|
||||
ResetIsRunningOnCompositor();
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
nsAutoAnimationMutationBatch mb(mTarget.mElement->OwnerDoc());
|
||||
if (mAnimation) {
|
||||
MutationObservers::NotifyAnimationRemoved(mAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
mTarget = newTarget;
|
||||
|
||||
if (mTarget) {
|
||||
UpdateTargetRegistration();
|
||||
RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle(Flush::None);
|
||||
if (computedStyle) {
|
||||
UpdateProperties(computedStyle);
|
||||
}
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
nsAutoAnimationMutationBatch mb(mTarget.mElement->OwnerDoc());
|
||||
if (mAnimation) {
|
||||
MutationObservers::NotifyAnimationAdded(mAnimation);
|
||||
mAnimation->ReschedulePendingTasks();
|
||||
}
|
||||
}
|
||||
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectTargetUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeEffect::UpdateTargetRegistration() {
|
||||
if (!mTarget) {
|
||||
return;
|
||||
@ -799,8 +910,8 @@ void KeyframeEffect::UpdateTargetRegistration() {
|
||||
"Out of date Animation::IsRelevant value");
|
||||
|
||||
if (isRelevant && !mInEffectSet) {
|
||||
EffectSet* effectSet = EffectSet::GetOrCreateEffectSet(
|
||||
mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetOrCreateEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
effectSet->AddEffect(*this);
|
||||
mInEffectSet = true;
|
||||
UpdateEffectSet(effectSet);
|
||||
@ -818,7 +929,7 @@ void KeyframeEffect::UnregisterTarget() {
|
||||
}
|
||||
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
MOZ_ASSERT(effectSet,
|
||||
"If mInEffectSet is true, there must be an EffectSet"
|
||||
" on the target element");
|
||||
@ -827,7 +938,7 @@ void KeyframeEffect::UnregisterTarget() {
|
||||
effectSet->RemoveEffect(*this);
|
||||
|
||||
if (effectSet->IsEmpty()) {
|
||||
EffectSet::DestroyEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet::DestroyEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
}
|
||||
}
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
@ -841,10 +952,10 @@ void KeyframeEffect::RequestRestyle(
|
||||
return;
|
||||
}
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(mTarget->mElement);
|
||||
nsContentUtils::GetContextForContent(mTarget.mElement);
|
||||
if (presContext && mAnimation) {
|
||||
presContext->EffectCompositor()->RequestRestyle(
|
||||
mTarget->mElement, mTarget->mPseudoType, aRestyleType,
|
||||
mTarget.mElement, mTarget.mPseudoType, aRestyleType,
|
||||
mAnimation->CascadeLevel());
|
||||
}
|
||||
}
|
||||
@ -858,17 +969,15 @@ already_AddRefed<ComputedStyle> KeyframeEffect::GetTargetComputedStyle(
|
||||
MOZ_ASSERT(mTarget,
|
||||
"Should only have a document when we have a target element");
|
||||
|
||||
nsAtom* pseudo =
|
||||
PseudoStyle::IsPseudoElement(mTarget->mPseudoType)
|
||||
? nsCSSPseudoElements::GetPseudoAtom(mTarget->mPseudoType)
|
||||
: nullptr;
|
||||
nsAtom* pseudo = PseudoStyle::IsPseudoElement(mTarget.mPseudoType)
|
||||
? nsCSSPseudoElements::GetPseudoAtom(mTarget.mPseudoType)
|
||||
: nullptr;
|
||||
|
||||
OwningAnimationTarget kungfuDeathGrip(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
OwningAnimationTarget kungfuDeathGrip(mTarget.mElement, mTarget.mPseudoType);
|
||||
|
||||
return aFlushType == Flush::Style
|
||||
? nsComputedDOMStyle::GetComputedStyle(mTarget->mElement, pseudo)
|
||||
: nsComputedDOMStyle::GetComputedStyleNoFlush(mTarget->mElement,
|
||||
? nsComputedDOMStyle::GetComputedStyle(mTarget.mElement, pseudo)
|
||||
: nsComputedDOMStyle::GetComputedStyleNoFlush(mTarget.mElement,
|
||||
pseudo);
|
||||
}
|
||||
|
||||
@ -891,8 +1000,7 @@ void DumpAnimationProperties(
|
||||
|
||||
/* static */
|
||||
already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
const GlobalObject& aGlobal, Element* aTarget,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv) {
|
||||
@ -901,8 +1009,7 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
|
||||
|
||||
/* static */
|
||||
already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
const GlobalObject& aGlobal, Element* aTarget,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aRv) {
|
||||
@ -925,8 +1032,8 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
|
||||
// Note: we don't need to re-throw exceptions since the value specified on
|
||||
// aSource's timing object can be assumed valid.
|
||||
RefPtr<KeyframeEffect> effect = new KeyframeEffect(
|
||||
doc, aSource.mTarget, TimingParams(aSource.SpecifiedTiming()),
|
||||
aSource.mEffectOptions);
|
||||
doc, OwningAnimationTarget(aSource.mTarget),
|
||||
TimingParams(aSource.SpecifiedTiming()), aSource.mEffectOptions);
|
||||
// Copy cumulative change hint. mCumulativeChangeHint should be the same as
|
||||
// the source one because both of targets are the same.
|
||||
effect->mCumulativeChangeHint = aSource.mCumulativeChangeHint;
|
||||
@ -936,76 +1043,45 @@ already_AddRefed<KeyframeEffect> KeyframeEffect::Constructor(
|
||||
// computed offsets and rebuild the animation properties.
|
||||
effect->mKeyframes = aSource.mKeyframes;
|
||||
effect->mProperties = aSource.mProperties;
|
||||
for (auto iter = aSource.mBaseValues.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
// XXX Should this use non-const Iter() and then pass
|
||||
// std::move(iter.Data())? Otherwise aSource might be a const&...
|
||||
effect->mBaseValues.Put(iter.Key(), RefPtr{iter.Data()});
|
||||
}
|
||||
return effect.forget();
|
||||
}
|
||||
|
||||
void KeyframeEffect::GetTarget(
|
||||
Nullable<OwningElementOrCSSPseudoElement>& aRv) const {
|
||||
if (!mTarget) {
|
||||
aRv.SetNull();
|
||||
void KeyframeEffect::SetPseudoElement(const nsAString& aPseudoElement,
|
||||
ErrorResult& aRv) {
|
||||
PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
|
||||
if (DOMStringIsNull(aPseudoElement)) {
|
||||
UpdateTarget(mTarget.mElement, pseudoType);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mTarget->mPseudoType) {
|
||||
case PseudoStyleType::before:
|
||||
case PseudoStyleType::after:
|
||||
case PseudoStyleType::marker:
|
||||
aRv.SetValue().SetAsCSSPseudoElement() =
|
||||
CSSPseudoElement::GetCSSPseudoElement(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
break;
|
||||
|
||||
case PseudoStyleType::NotPseudo:
|
||||
aRv.SetValue().SetAsElement() = mTarget->mElement;
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Animation of unsupported pseudo-type");
|
||||
aRv.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeEffect::SetTarget(
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget) {
|
||||
Maybe<OwningAnimationTarget> newTarget = ConvertTarget(aTarget);
|
||||
if (mTarget == newTarget) {
|
||||
// Assign the same target, skip it.
|
||||
// Note: GetPseudoAtom() also returns nullptr for the null string,
|
||||
// so we handle null case before this.
|
||||
RefPtr<nsAtom> pseudoAtom =
|
||||
nsCSSPseudoElements::GetPseudoAtom(aPseudoElement);
|
||||
if (!pseudoAtom) {
|
||||
// Per the spec, we throw SyntaxError for syntactically invalid pseudos.
|
||||
aRv.ThrowSyntaxError(
|
||||
nsPrintfCString("'%s' is a syntactically invalid pseudo-element.",
|
||||
NS_ConvertUTF16toUTF8(aPseudoElement).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTarget) {
|
||||
UnregisterTarget();
|
||||
ResetIsRunningOnCompositor();
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc());
|
||||
if (mAnimation) {
|
||||
MutationObservers::NotifyAnimationRemoved(mAnimation);
|
||||
}
|
||||
pseudoType = nsCSSPseudoElements::GetPseudoType(
|
||||
pseudoAtom, CSSEnabledState::ForAllContent);
|
||||
if (!IsSupportedPseudoForWebAnimation(pseudoType)) {
|
||||
// Per the spec, we throw SyntaxError for unsupported pseudos.
|
||||
aRv.ThrowSyntaxError(
|
||||
nsPrintfCString("'%s' is an unsupported pseudo-element.",
|
||||
NS_ConvertUTF16toUTF8(aPseudoElement).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
mTarget = newTarget;
|
||||
|
||||
if (mTarget) {
|
||||
UpdateTargetRegistration();
|
||||
RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle(Flush::None);
|
||||
if (computedStyle) {
|
||||
UpdateProperties(computedStyle);
|
||||
}
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc());
|
||||
if (mAnimation) {
|
||||
MutationObservers::NotifyAnimationAdded(mAnimation);
|
||||
mAnimation->ReschedulePendingTasks();
|
||||
}
|
||||
}
|
||||
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectTargetUpdated();
|
||||
}
|
||||
UpdateTarget(mTarget.mElement, pseudoType);
|
||||
}
|
||||
|
||||
static void CreatePropertyValue(
|
||||
@ -1064,9 +1140,14 @@ void KeyframeEffect::GetProperties(
|
||||
if (segment.mFromKey == segment.mToKey) {
|
||||
fromValue.mEasing.Reset();
|
||||
}
|
||||
// The following won't fail since we have already allocated the capacity
|
||||
// above.
|
||||
propertyDetails.mValues.AppendElement(fromValue, mozilla::fallible);
|
||||
// Even though we called SetCapacity before, this could fail, since we
|
||||
// might add multiple elements to propertyDetails.mValues for an element
|
||||
// of property.mSegments in the cases mentioned below.
|
||||
if (!propertyDetails.mValues.AppendElement(fromValue,
|
||||
mozilla::fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normally we can ignore the to-value for this segment since it is
|
||||
// identical to the from-value from the next segment. However, we need
|
||||
@ -1082,7 +1163,11 @@ void KeyframeEffect::GetProperties(
|
||||
// last property value or before a sudden jump so we just drop the
|
||||
// easing property altogether.
|
||||
toValue.mEasing.Reset();
|
||||
propertyDetails.mValues.AppendElement(toValue, mozilla::fallible);
|
||||
if (!propertyDetails.mValues.AppendElement(toValue,
|
||||
mozilla::fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1090,7 +1175,7 @@ void KeyframeEffect::GetProperties(
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeEffect::GetKeyframes(JSContext*& aCx, nsTArray<JSObject*>& aResult,
|
||||
void KeyframeEffect::GetKeyframes(JSContext* aCx, nsTArray<JSObject*>& aResult,
|
||||
ErrorResult& aRv) const {
|
||||
MOZ_ASSERT(aResult.IsEmpty());
|
||||
MOZ_ASSERT(!aRv.Failed());
|
||||
@ -1140,6 +1225,8 @@ void KeyframeEffect::GetKeyframes(JSContext*& aCx, nsTArray<JSObject*>& aResult,
|
||||
keyframe.mTimingFunction.ref().AppendToString(keyframeDict.mEasing);
|
||||
} // else if null, leave easing as its default "linear".
|
||||
|
||||
// With the pref off (i.e. dom.animations-api.compositing.enabled:false),
|
||||
// the dictionary-to-JS conversion will skip this member entirely.
|
||||
keyframeDict.mComposite = keyframe.mComposite;
|
||||
|
||||
JS::Rooted<JS::Value> keyframeJSValue(aCx);
|
||||
@ -1227,6 +1314,46 @@ KeyframeEffect::OverflowRegionRefreshInterval() {
|
||||
return kOverflowRegionRefreshInterval;
|
||||
}
|
||||
|
||||
static bool IsDefinitivelyInvisibleDueToOpacity(const nsIFrame& aFrame) {
|
||||
if (!aFrame.Style()->IsInOpacityZeroSubtree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the root of the opacity: 0 subtree.
|
||||
const nsIFrame* root = &aFrame;
|
||||
while (true) {
|
||||
auto* parent = root->GetInFlowParent();
|
||||
if (!parent || !parent->Style()->IsInOpacityZeroSubtree()) {
|
||||
break;
|
||||
}
|
||||
root = parent;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(root && root->Style()->IsInOpacityZeroSubtree());
|
||||
|
||||
// If aFrame is the root of the opacity: zero subtree, we can't prove we can
|
||||
// optimize it away, because it may have an opacity animation itself.
|
||||
if (root == &aFrame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even if we're in an opacity: zero subtree, if the root of the subtree may
|
||||
// have an opacity animation, we can't optimize us away, as we may become
|
||||
// visible ourselves.
|
||||
return !root->HasAnimationOfOpacity();
|
||||
}
|
||||
|
||||
static bool CanOptimizeAwayDueToOpacity(const KeyframeEffect& aEffect,
|
||||
const nsIFrame& aFrame) {
|
||||
if (!aFrame.Style()->IsInOpacityZeroSubtree()) {
|
||||
return false;
|
||||
}
|
||||
if (IsDefinitivelyInvisibleDueToOpacity(aFrame)) {
|
||||
return true;
|
||||
}
|
||||
return !aEffect.HasOpacityChange() && !aFrame.HasAnimationOfOpacity();
|
||||
}
|
||||
|
||||
bool KeyframeEffect::CanThrottleIfNotVisible(nsIFrame& aFrame) const {
|
||||
// Unless we are newly in-effect, we can throttle the animation if the
|
||||
// animation is paint only and the target frame is out of view or the document
|
||||
@ -1242,8 +1369,13 @@ bool KeyframeEffect::CanThrottleIfNotVisible(nsIFrame& aFrame) const {
|
||||
|
||||
const bool isVisibilityHidden =
|
||||
!aFrame.IsVisibleOrMayHaveVisibleDescendants();
|
||||
if ((!isVisibilityHidden || HasVisibilityChange()) &&
|
||||
!aFrame.IsScrolledOutOfView()) {
|
||||
const bool canOptimizeAwayVisibility =
|
||||
isVisibilityHidden && !HasVisibilityChange();
|
||||
|
||||
const bool invisible = canOptimizeAwayVisibility ||
|
||||
CanOptimizeAwayDueToOpacity(*this, aFrame) ||
|
||||
aFrame.IsScrolledOutOfView();
|
||||
if (!invisible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1302,7 +1434,7 @@ bool KeyframeEffect::CanThrottle() const {
|
||||
"The property should be able to run on the compositor");
|
||||
if (!effectSet) {
|
||||
effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
MOZ_ASSERT(effectSet,
|
||||
"CanThrottle should be called on an effect "
|
||||
"associated with a target element");
|
||||
@ -1338,7 +1470,7 @@ bool KeyframeEffect::CanThrottleOverflowChanges(const nsIFrame& aFrame) const {
|
||||
TimeStamp now = aFrame.PresContext()->RefreshDriver()->MostRecentRefresh();
|
||||
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
MOZ_ASSERT(effectSet,
|
||||
"CanOverflowTransformChanges is expected to be called"
|
||||
" on an effect in an effect set");
|
||||
@ -1367,7 +1499,7 @@ bool KeyframeEffect::CanThrottleOverflowChangesInScrollable(
|
||||
|
||||
// If we don't show scrollbars and have no intersection observers, we don't
|
||||
// care about overflow.
|
||||
if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars) == 0 &&
|
||||
if (LookAndFeel::GetInt(LookAndFeel::IntID::ShowHideScrollbars) == 0 &&
|
||||
!hasIntersectionObservers) {
|
||||
return true;
|
||||
}
|
||||
@ -1415,16 +1547,16 @@ nsIFrame* KeyframeEffect::GetPrimaryFrame() const {
|
||||
return frame;
|
||||
}
|
||||
|
||||
if (mTarget->mPseudoType == PseudoStyleType::before) {
|
||||
frame = nsLayoutUtils::GetBeforeFrame(mTarget->mElement);
|
||||
} else if (mTarget->mPseudoType == PseudoStyleType::after) {
|
||||
frame = nsLayoutUtils::GetAfterFrame(mTarget->mElement);
|
||||
} else if (mTarget->mPseudoType == PseudoStyleType::marker) {
|
||||
frame = nsLayoutUtils::GetMarkerFrame(mTarget->mElement);
|
||||
if (mTarget.mPseudoType == PseudoStyleType::before) {
|
||||
frame = nsLayoutUtils::GetBeforeFrame(mTarget.mElement);
|
||||
} else if (mTarget.mPseudoType == PseudoStyleType::after) {
|
||||
frame = nsLayoutUtils::GetAfterFrame(mTarget.mElement);
|
||||
} else if (mTarget.mPseudoType == PseudoStyleType::marker) {
|
||||
frame = nsLayoutUtils::GetMarkerFrame(mTarget.mElement);
|
||||
} else {
|
||||
frame = mTarget->mElement->GetPrimaryFrame();
|
||||
MOZ_ASSERT(mTarget->mPseudoType == PseudoStyleType::NotPseudo,
|
||||
"unknown mTarget->mPseudoType");
|
||||
frame = mTarget.mElement->GetPrimaryFrame();
|
||||
MOZ_ASSERT(mTarget.mPseudoType == PseudoStyleType::NotPseudo,
|
||||
"unknown mTarget.mPseudoType");
|
||||
}
|
||||
|
||||
return frame;
|
||||
@ -1434,7 +1566,7 @@ Document* KeyframeEffect::GetRenderedDocument() const {
|
||||
if (!mTarget) {
|
||||
return nullptr;
|
||||
}
|
||||
return mTarget->mElement->GetComposedDoc();
|
||||
return mTarget.mElement->GetComposedDoc();
|
||||
}
|
||||
|
||||
PresShell* KeyframeEffect::GetPresShell() const {
|
||||
@ -1516,7 +1648,7 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
|
||||
const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet,
|
||||
AnimationPerformanceWarning::Type& aPerformanceWarning /* out */) const {
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
// The various transform properties ('transform', 'scale' etc.) get combined
|
||||
// on the compositor.
|
||||
//
|
||||
@ -1535,6 +1667,10 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool enableMainthreadSynchronizationWithGeometricAnimations =
|
||||
StaticPrefs::
|
||||
dom_animations_mainthread_synchronization_with_geometric_animations();
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
// If there is a property for animations level that is overridden by
|
||||
// !important rules, it should not block other animations from running
|
||||
@ -1551,7 +1687,8 @@ bool KeyframeEffect::ShouldBlockAsyncTransformAnimations(
|
||||
continue;
|
||||
}
|
||||
// Check for geometric properties
|
||||
if (IsGeometricProperty(property.mProperty)) {
|
||||
if (enableMainthreadSynchronizationWithGeometricAnimations &&
|
||||
IsGeometricProperty(property.mProperty)) {
|
||||
aPerformanceWarning =
|
||||
AnimationPerformanceWarning::Type::TransformWithGeometricProperties;
|
||||
return true;
|
||||
@ -1587,7 +1724,7 @@ void KeyframeEffect::SetPerformanceWarning(
|
||||
if (!curr.HasProperty(property.mProperty)) {
|
||||
continue;
|
||||
}
|
||||
property.SetPerformanceWarning(aWarning, mTarget->mElement);
|
||||
property.SetPerformanceWarning(aWarning, mTarget.mElement);
|
||||
curr.RemoveProperty(property.mProperty);
|
||||
if (curr.IsEmpty()) {
|
||||
return;
|
||||
@ -1604,7 +1741,7 @@ KeyframeEffect::CreateComputedStyleForAnimationValue(
|
||||
"with a valid ComputedStyle");
|
||||
|
||||
Element* elementForResolve = EffectCompositor::GetElementToRestyle(
|
||||
mTarget->mElement, mTarget->mPseudoType);
|
||||
mTarget.mElement, mTarget.mPseudoType);
|
||||
// The element may be null if, for example, we target a pseudo-element that no
|
||||
// longer exists.
|
||||
if (!elementForResolve) {
|
||||
@ -1622,7 +1759,7 @@ void KeyframeEffect::CalculateCumulativeChangeHint(
|
||||
mNeedsStyleData = false;
|
||||
|
||||
nsPresContext* presContext =
|
||||
mTarget ? nsContentUtils::GetContextForContent(mTarget->mElement)
|
||||
mTarget ? nsContentUtils::GetContextForContent(mTarget.mElement)
|
||||
: nullptr;
|
||||
if (!presContext) {
|
||||
// Change hints make no sense if we're not rendered.
|
||||
@ -1715,7 +1852,7 @@ void KeyframeEffect::SetAnimation(Animation* aAnimation) {
|
||||
}
|
||||
|
||||
bool KeyframeEffect::CanIgnoreIfNotVisible() const {
|
||||
if (!AnimationUtils::IsOffscreenThrottlingEnabled()) {
|
||||
if (!StaticPrefs::dom_animations_offscreen_throttling()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1731,7 +1868,7 @@ void KeyframeEffect::MarkCascadeNeedsUpdate() {
|
||||
}
|
||||
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
if (!effectSet) {
|
||||
return;
|
||||
}
|
||||
@ -1785,14 +1922,11 @@ bool KeyframeEffect::ContainsAnimatedScale(const nsIFrame* aFrame) const {
|
||||
}
|
||||
|
||||
AnimationValue baseStyle = BaseStyle(prop.mProperty);
|
||||
if (baseStyle.IsNull()) {
|
||||
// If we failed to get the base style, we consider it has scale value
|
||||
// here just to be safe.
|
||||
return true;
|
||||
}
|
||||
gfx::Size size = baseStyle.GetScaleValue(aFrame);
|
||||
if (size != gfx::Size(1.0f, 1.0f)) {
|
||||
return true;
|
||||
if (!baseStyle.IsNull()) {
|
||||
gfx::Size size = baseStyle.GetScaleValue(aFrame);
|
||||
if (size != gfx::Size(1.0f, 1.0f)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// This is actually overestimate because there are some cases that combining
|
||||
@ -1825,7 +1959,7 @@ void KeyframeEffect::UpdateEffectSet(EffectSet* aEffectSet) const {
|
||||
EffectSet* effectSet =
|
||||
aEffectSet
|
||||
? aEffectSet
|
||||
: EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
: EffectSet::GetEffectSet(mTarget.mElement, mTarget.mPseudoType);
|
||||
if (!effectSet) {
|
||||
return;
|
||||
}
|
||||
@ -1878,6 +2012,7 @@ KeyframeEffect::MatchForCompositor KeyframeEffect::IsMatchForCompositor(
|
||||
// If we know that the animation is not visible, we don't need to send the
|
||||
// animation to the compositor.
|
||||
if (!aFrame->IsVisibleOrMayHaveVisibleDescendants() ||
|
||||
IsDefinitivelyInvisibleDueToOpacity(*aFrame) ||
|
||||
aFrame->IsScrolledOutOfView()) {
|
||||
return KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty;
|
||||
}
|
||||
@ -1899,6 +2034,13 @@ KeyframeEffect::MatchForCompositor KeyframeEffect::IsMatchForCompositor(
|
||||
}
|
||||
}
|
||||
|
||||
// We can't run this background color animation on the compositor if there
|
||||
// is any `current-color` keyframe.
|
||||
if (mHasCurrentColor) {
|
||||
aPerformanceWarning = AnimationPerformanceWarning::Type::HasCurrentColor;
|
||||
return KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty;
|
||||
}
|
||||
|
||||
return mAnimation->IsPlaying() ? KeyframeEffect::MatchForCompositor::Yes
|
||||
: KeyframeEffect::MatchForCompositor::IfNeeded;
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ class nsIFrame;
|
||||
namespace mozilla {
|
||||
|
||||
class AnimValuesStyleRule;
|
||||
enum class PseudoStyleType : uint8_t;
|
||||
class ErrorResult;
|
||||
struct AnimationRule;
|
||||
struct TimingParams;
|
||||
@ -46,9 +45,7 @@ class ComputedStyle;
|
||||
class PresShell;
|
||||
|
||||
namespace dom {
|
||||
class ElementOrCSSPseudoElement;
|
||||
class GlobalObject;
|
||||
class OwningElementOrCSSPseudoElement;
|
||||
class UnrestrictedDoubleOrKeyframeAnimationOptions;
|
||||
class UnrestrictedDoubleOrKeyframeEffectOptions;
|
||||
enum class IterationCompositeOperation : uint8_t;
|
||||
@ -102,8 +99,6 @@ struct AnimationProperty {
|
||||
const dom::Element* aElement);
|
||||
};
|
||||
|
||||
struct ElementPropertyTransition;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Animation;
|
||||
@ -111,8 +106,7 @@ class Document;
|
||||
|
||||
class KeyframeEffect : public AnimationEffect {
|
||||
public:
|
||||
KeyframeEffect(Document* aDocument,
|
||||
const Maybe<OwningAnimationTarget>& aTarget,
|
||||
KeyframeEffect(Document* aDocument, OwningAnimationTarget&& aTarget,
|
||||
TimingParams&& aTiming, const KeyframeEffectParams& aOptions);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
@ -126,8 +120,7 @@ class KeyframeEffect : public AnimationEffect {
|
||||
|
||||
// KeyframeEffect interface
|
||||
static already_AddRefed<KeyframeEffect> Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
const GlobalObject& aGlobal, Element* aTarget,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
@ -139,28 +132,37 @@ class KeyframeEffect : public AnimationEffect {
|
||||
// for use with for Animatable.animate.
|
||||
// Not exposed to content.
|
||||
static already_AddRefed<KeyframeEffect> Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
const GlobalObject& aGlobal, Element* aTarget,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
|
||||
Maybe<NonOwningAnimationTarget> GetTarget() const {
|
||||
Maybe<NonOwningAnimationTarget> result;
|
||||
if (mTarget) {
|
||||
result.emplace(*mTarget);
|
||||
}
|
||||
return result;
|
||||
already_AddRefed<Element> GetTarget() const {
|
||||
RefPtr<Element> ret = mTarget.mElement;
|
||||
return ret.forget();
|
||||
}
|
||||
// This method calls GetTargetComputedStyle which is not safe to use when
|
||||
NonOwningAnimationTarget GetAnimationTarget() const {
|
||||
return NonOwningAnimationTarget(mTarget.mElement, mTarget.mPseudoType);
|
||||
}
|
||||
void GetPseudoElement(nsAString& aRetVal) const {
|
||||
if (mTarget.mPseudoType == PseudoStyleType::NotPseudo) {
|
||||
SetDOMStringToNull(aRetVal);
|
||||
return;
|
||||
}
|
||||
aRetVal = nsCSSPseudoElements::PseudoTypeAsString(mTarget.mPseudoType);
|
||||
}
|
||||
|
||||
// These two setters call GetTargetComputedStyle which is not safe to use when
|
||||
// we are in the middle of updating style. If we need to use this when
|
||||
// updating style, we should pass the ComputedStyle into this method and use
|
||||
// that to update the properties rather than calling
|
||||
// GetComputedStyle.
|
||||
void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
|
||||
void SetTarget(Element* aTarget) {
|
||||
UpdateTarget(aTarget, mTarget.mPseudoType);
|
||||
}
|
||||
void SetPseudoElement(const nsAString& aPseudoElement, ErrorResult& aRv);
|
||||
|
||||
void GetKeyframes(JSContext*& aCx, nsTArray<JSObject*>& aResult,
|
||||
void GetKeyframes(JSContext* aCx, nsTArray<JSObject*>& aResult,
|
||||
ErrorResult& aRv) const;
|
||||
void GetProperties(nsTArray<AnimationPropertyDetails>& aProperties,
|
||||
ErrorResult& aRv) const;
|
||||
@ -176,11 +178,15 @@ class KeyframeEffect : public AnimationEffect {
|
||||
void NotifyAnimationTimingUpdated(PostRestyleMode aPostRestyle);
|
||||
void RequestRestyle(EffectCompositor::RestyleType aRestyleType);
|
||||
void SetAnimation(Animation* aAnimation) override;
|
||||
void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
|
||||
ErrorResult& aRv);
|
||||
virtual void SetKeyframes(JSContext* aContext,
|
||||
JS::Handle<JSObject*> aKeyframes, ErrorResult& aRv);
|
||||
void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
|
||||
const ComputedStyle* aStyle);
|
||||
|
||||
// Replace the start value of the transition. This is used for updating
|
||||
// transitions running on the compositor.
|
||||
void ReplaceTransitionStartValue(AnimationValue&& aStartValue);
|
||||
|
||||
// Returns the set of properties affected by this effect regardless of
|
||||
// whether any of these properties is overridden by an !important rule.
|
||||
nsCSSPropertyIDSet GetPropertySet() const;
|
||||
@ -277,7 +283,7 @@ class KeyframeEffect : public AnimationEffect {
|
||||
AnimationPerformanceWarning::Type& aPerformanceWarning /* out */) const;
|
||||
bool HasGeometricProperties() const;
|
||||
bool AffectsGeometry() const override {
|
||||
return GetTarget() && HasGeometricProperties();
|
||||
return mTarget && HasGeometricProperties();
|
||||
}
|
||||
|
||||
Document* GetRenderedDocument() const;
|
||||
@ -341,16 +347,16 @@ class KeyframeEffect : public AnimationEffect {
|
||||
const Nullable<double>& aProgressOnLastCompose,
|
||||
uint64_t aCurrentIterationOnLastCompose);
|
||||
|
||||
bool HasOpacityChange() const {
|
||||
return mCumulativeChangeHint & nsChangeHint_UpdateOpacityLayer;
|
||||
}
|
||||
|
||||
protected:
|
||||
~KeyframeEffect() override = default;
|
||||
|
||||
static Maybe<OwningAnimationTarget> ConvertTarget(
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget);
|
||||
|
||||
template <class OptionsType>
|
||||
static already_AddRefed<KeyframeEffect> ConstructKeyframeEffect(
|
||||
const GlobalObject& aGlobal,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
const GlobalObject& aGlobal, Element* aTarget,
|
||||
JS::Handle<JSObject*> aKeyframes, const OptionsType& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
@ -359,6 +365,9 @@ class KeyframeEffect : public AnimationEffect {
|
||||
// needed.
|
||||
nsTArray<AnimationProperty> BuildProperties(const ComputedStyle* aStyle);
|
||||
|
||||
// Helper for SetTarget() and SetPseudoElement().
|
||||
void UpdateTarget(Element* aElement, PseudoStyleType aPseudoType);
|
||||
|
||||
// This effect is registered with its target element so long as:
|
||||
//
|
||||
// (a) It has a target element, and
|
||||
@ -397,7 +406,7 @@ class KeyframeEffect : public AnimationEffect {
|
||||
const ComputedStyle* aComputedValues,
|
||||
RefPtr<ComputedStyle>& aBaseComputedValues);
|
||||
|
||||
Maybe<OwningAnimationTarget> mTarget;
|
||||
OwningAnimationTarget mTarget;
|
||||
|
||||
KeyframeEffectParams mEffectOptions;
|
||||
|
||||
@ -432,6 +441,9 @@ class KeyframeEffect : public AnimationEffect {
|
||||
// if our properties haven't changed.
|
||||
bool mNeedsStyleData = false;
|
||||
|
||||
// True if there is any current-color for background color in this keyframes.
|
||||
bool mHasCurrentColor = false;
|
||||
|
||||
// The non-animated values for properties in this effect that contain at
|
||||
// least one animation value that is composited with the underlying value
|
||||
// (i.e. it uses the additive or accumulate composite mode).
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define mozilla_KeyframeEffectParams_h
|
||||
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h" // IterationCompositeOperation
|
||||
#include "mozilla/PseudoStyleType.h" // PseudoStyleType
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -13,6 +14,7 @@ struct KeyframeEffectParams {
|
||||
dom::IterationCompositeOperation mIterationComposite =
|
||||
dom::IterationCompositeOperation::Replace;
|
||||
dom::CompositeOperation mComposite = dom::CompositeOperation::Replace;
|
||||
PseudoStyleType mPseudoType = PseudoStyleType::NotPseudo;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -4,33 +4,35 @@
|
||||
|
||||
#include "mozilla/KeyframeUtils.h"
|
||||
|
||||
#include <algorithm> // For std::stable_sort, std::min
|
||||
|
||||
#include "js/ForOfIterator.h" // For JS::ForOfIterator
|
||||
#include "jsapi.h" // For most JSAPI
|
||||
#include "mozilla/ComputedStyle.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/RangedArray.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "mozilla/ServoBindingTypes.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "mozilla/ServoCSSParser.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/TimingParams.h"
|
||||
#include "mozilla/dom/BaseKeyframeTypesBinding.h" // For FastBaseKeyframe etc.
|
||||
#include "mozilla/dom/BindingCallContext.h"
|
||||
#include "mozilla/dom/Document.h" // For Document::AreWebAnimationsImplicitKeyframesEnabled
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h"
|
||||
#include "mozilla/dom/KeyframeEffect.h" // For PropertyValuesPair etc.
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "jsapi.h" // For most JSAPI
|
||||
#include "js/ForOfIterator.h" // For JS::ForOfIterator
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsContentUtils.h" // For GetContextForContent
|
||||
#include "nsCSSPropertyIDSet.h"
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsCSSPseudoElements.h" // For PseudoStyleType
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsContentUtils.h" // For GetContextForContent
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsPresContextInlines.h"
|
||||
#include "nsTArray.h"
|
||||
#include <algorithm> // For std::stable_sort, std::min
|
||||
|
||||
using mozilla::dom::Nullable;
|
||||
|
||||
@ -380,7 +382,10 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
|
||||
const char* aContext,
|
||||
nsTArray<Keyframe>& aResult) {
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
ErrorResult parseEasingResult;
|
||||
// Parsing errors should only be reported after we have finished iterating
|
||||
// through all values. If we have any early returns while iterating, we should
|
||||
// ignore parsing errors.
|
||||
IgnoredErrorResult parseEasingResult;
|
||||
|
||||
for (;;) {
|
||||
bool done;
|
||||
@ -395,16 +400,20 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
|
||||
// value).
|
||||
if (!value.isObject() && !value.isNullOrUndefined()) {
|
||||
dom::ThrowErrorMessage<dom::MSG_NOT_OBJECT>(
|
||||
aCx,
|
||||
nsPrintfCString("%sElement of sequence<Keyframe> argument", aContext)
|
||||
.get());
|
||||
aCx, aContext, "Element of sequence<Keyframe> argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the JS value into a BaseKeyframe dictionary value.
|
||||
dom::binding_detail::FastBaseKeyframe keyframeDict;
|
||||
if (!keyframeDict.Init(aCx, value,
|
||||
dom::BindingCallContext callCx(aCx, aContext);
|
||||
if (!keyframeDict.Init(callCx, value,
|
||||
"Element of sequence<Keyframe> argument")) {
|
||||
// This may happen if the value type of the member of BaseKeyframe is
|
||||
// invalid. e.g. `offset` only accept a double value, so if we provide a
|
||||
// string, we enter this branch.
|
||||
// Besides, keyframeDict.Init() should throw a Type Error message already,
|
||||
// so we don't have to do it again.
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -412,6 +421,7 @@ static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
|
||||
if (!keyframe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!keyframeDict.mOffset.IsNull()) {
|
||||
keyframe->mOffset.emplace(keyframeDict.mOffset.Value());
|
||||
}
|
||||
@ -969,8 +979,8 @@ static void GetKeyframeListFromPropertyIndexedKeyframe(
|
||||
// Convert the object to a property-indexed keyframe dictionary to
|
||||
// get its explicit dictionary members.
|
||||
dom::binding_detail::FastBasePropertyIndexedKeyframe keyframeDict;
|
||||
if (!keyframeDict.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument",
|
||||
false)) {
|
||||
// XXXbz Pass in the method name from callers and set up a BindingCallContext?
|
||||
if (!keyframeDict.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument")) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class PendingAnimationTracker final {
|
||||
void MarkAnimationsThatMightNeedSynchronization();
|
||||
|
||||
private:
|
||||
~PendingAnimationTracker() {}
|
||||
~PendingAnimationTracker() = default;
|
||||
|
||||
void EnsurePaintIsScheduled();
|
||||
|
||||
|
@ -46,7 +46,8 @@ TimingParams TimingParams::FromOptionsType(const OptionsType& aOptions,
|
||||
result.mDuration.emplace(
|
||||
StickyTimeDuration::FromMilliseconds(durationInMs));
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
nsPrintfCString error("Duration value %g is less than 0", durationInMs);
|
||||
aRv.ThrowTypeError(error);
|
||||
return result;
|
||||
}
|
||||
result.Update();
|
||||
@ -196,7 +197,8 @@ Maybe<ComputedTimingFunction> TimingParams::ParseEasing(
|
||||
nsTimingFunction timingFunction;
|
||||
RefPtr<URLExtraData> url = ServoCSSParser::GetURLExtraData(aDocument);
|
||||
if (!ServoCSSParser::ParseEasing(aEasing, url, timingFunction)) {
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(aEasing);
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(
|
||||
NS_ConvertUTF16toUTF8(aEasing));
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define mozilla_TimingParams_h
|
||||
|
||||
#include "nsStringFwd.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/dom/UnionTypes.h" // For OwningUnrestrictedDoubleOrString
|
||||
#include "mozilla/ComputedTimingFunction.h"
|
||||
@ -88,27 +89,33 @@ struct TimingParams {
|
||||
if (durationInMs >= 0) {
|
||||
result.emplace(StickyTimeDuration::FromMilliseconds(durationInMs));
|
||||
} else {
|
||||
aRv.ThrowTypeError<dom::MSG_ENFORCE_RANGE_OUT_OF_RANGE>(
|
||||
NS_LITERAL_STRING("duration"));
|
||||
nsPrintfCString err("Duration (%g) must be nonnegative", durationInMs);
|
||||
aRv.ThrowTypeError(err);
|
||||
}
|
||||
} else if (!aDuration.GetAsString().EqualsLiteral("auto")) {
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>(
|
||||
aDuration.GetAsString());
|
||||
NS_ConvertUTF16toUTF8(aDuration.GetAsString()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ValidateIterationStart(double aIterationStart, ErrorResult& aRv) {
|
||||
if (aIterationStart < 0) {
|
||||
aRv.ThrowTypeError<dom::MSG_ENFORCE_RANGE_OUT_OF_RANGE>(
|
||||
NS_LITERAL_STRING("iterationStart"));
|
||||
nsPrintfCString err("Iteration start (%g) must not be negative",
|
||||
aIterationStart);
|
||||
aRv.ThrowTypeError(err);
|
||||
}
|
||||
}
|
||||
|
||||
static void ValidateIterations(double aIterations, ErrorResult& aRv) {
|
||||
if (IsNaN(aIterations) || aIterations < 0) {
|
||||
aRv.ThrowTypeError<dom::MSG_ENFORCE_RANGE_OUT_OF_RANGE>(
|
||||
NS_LITERAL_STRING("iterations"));
|
||||
if (IsNaN(aIterations)) {
|
||||
aRv.ThrowTypeError("Iterations must not be NaN");
|
||||
return;
|
||||
}
|
||||
|
||||
if (aIterations < 0) {
|
||||
nsPrintfCString err("Iterations (%g) must not be negative", aIterations);
|
||||
aRv.ThrowTypeError(err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,6 +159,10 @@ struct TimingParams {
|
||||
mDuration = std::move(aDuration);
|
||||
Update();
|
||||
}
|
||||
void SetDuration(const Maybe<StickyTimeDuration>& aDuration) {
|
||||
mDuration = aDuration;
|
||||
Update();
|
||||
}
|
||||
const Maybe<StickyTimeDuration>& Duration() const { return mDuration; }
|
||||
|
||||
void SetDelay(const TimeDuration& aDelay) {
|
||||
|
@ -12,7 +12,9 @@ EXPORTS.mozilla.dom += [
|
||||
'Animation.h',
|
||||
'AnimationEffect.h',
|
||||
'AnimationTimeline.h',
|
||||
'CSSAnimation.h',
|
||||
'CSSPseudoElement.h',
|
||||
'CSSTransition.h',
|
||||
'DocumentTimeline.h',
|
||||
'KeyframeEffect.h',
|
||||
]
|
||||
@ -45,7 +47,9 @@ UNIFIED_SOURCES += [
|
||||
'AnimationTimeline.cpp',
|
||||
'AnimationUtils.cpp',
|
||||
'ComputedTimingFunction.cpp',
|
||||
'CSSAnimation.cpp',
|
||||
'CSSPseudoElement.cpp',
|
||||
'CSSTransition.cpp',
|
||||
'DocumentTimeline.cpp',
|
||||
'EffectCompositor.cpp',
|
||||
'EffectSet.cpp',
|
||||
@ -61,4 +65,6 @@ LOCAL_INCLUDES += [
|
||||
'/layout/style',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -502,13 +502,14 @@ promise_test(t => {
|
||||
var childBAnimations = childB.getAnimations();
|
||||
|
||||
var divBeforeAnimations =
|
||||
docAnims.filter(x => (x.effect.target.element == div &&
|
||||
x.effect.target.type == "::before"));
|
||||
docAnims.filter(x => (x.effect.target == div &&
|
||||
x.effect.pseudoElement == "::before"));
|
||||
var divAfterAnimations =
|
||||
docAnims.filter(x => (x.effect.target.element == div &&
|
||||
x.effect.target.type == "::after"));
|
||||
docAnims.filter(x => (x.effect.target == div &&
|
||||
x.effect.pseudoElement == "::after"));
|
||||
var childBPseudoAnimations =
|
||||
docAnims.filter(x => x.effect.target.element == childB);
|
||||
docAnims.filter(x => (x.effect.target == childB &&
|
||||
x.effect.pseudoElement == "::before"));
|
||||
|
||||
var seekRecords;
|
||||
// The order in which we get the corresponding records is currently
|
||||
|
@ -74,21 +74,6 @@ function assert_equals_records(actual, expected, desc) {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a pseudo element
|
||||
function createPseudo(test, element, type) {
|
||||
addStyle(test, { '@keyframes anim': '',
|
||||
['.pseudo::' + type]: 'animation: anim 10s; ' +
|
||||
'content: \'\';' });
|
||||
element.classList.add('pseudo');
|
||||
var anims = document.getAnimations();
|
||||
assert_true(anims.length >= 1);
|
||||
var anim = anims[anims.length - 1];
|
||||
assert_equals(anim.effect.target.element, element);
|
||||
assert_equals(anim.effect.target.type, '::' + type);
|
||||
anim.cancel();
|
||||
return anim.effect.target;
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
[ { subtree: false },
|
||||
{ subtree: true }
|
||||
@ -1518,10 +1503,11 @@ function runTest() {
|
||||
|
||||
test(t => {
|
||||
var div = addDiv(t);
|
||||
var pseudoTarget = createPseudo(t, div, 'before');
|
||||
var observer = setupSynchronousObserver(t, div, true);
|
||||
|
||||
var anim = pseudoTarget.animate({ opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
|
||||
var anim = div.animate({ opacity: [ 0, 1 ] },
|
||||
{ duration: 200 * MS_PER_SEC,
|
||||
pseudoElement: '::before' });
|
||||
|
||||
assert_equals_records(observer.takeRecords(),
|
||||
[{ added: [anim], changed: [], removed: [] }],
|
||||
@ -1561,13 +1547,13 @@ function runTest() {
|
||||
|
||||
test(t => {
|
||||
var div = addDiv(t);
|
||||
var pseudoTarget = createPseudo(t, div, 'before');
|
||||
var observer = setupSynchronousObserver(t, div, false);
|
||||
|
||||
var anim = div.animate({ opacity: [ 0, 1 ] },
|
||||
{ duration: 100 * MS_PER_SEC });
|
||||
var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] },
|
||||
{ duration: 100 * MS_PER_SEC });
|
||||
var pAnim = div.animate({ opacity: [ 0, 1 ] },
|
||||
{ duration: 100 * MS_PER_SEC,
|
||||
pseudoElement: "::before" });
|
||||
|
||||
assert_equals_records(observer.takeRecords(),
|
||||
[{ added: [anim], changed: [], removed: [] }],
|
||||
|
@ -1644,6 +1644,27 @@ function testImportantRuleOverride() {
|
||||
'compositor because any of the properties has important rules');
|
||||
}
|
||||
|
||||
function testCurrentColor() {
|
||||
if (SpecialPowers.DOMWindowUtils.layerManagerType == 'WebRender') {
|
||||
return; // skip this test until bug 1510030 landed.
|
||||
}
|
||||
promise_test(async t => {
|
||||
const animation = addDivAndAnimate(t, { class: 'compositable' },
|
||||
{ backgroundColor: [ 'currentColor',
|
||||
'red' ] },
|
||||
100 * MS_PER_SEC);
|
||||
await waitForPaints();
|
||||
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getProperties(),
|
||||
[ { property: 'background-color',
|
||||
runningOnCompositor: false,
|
||||
warning: 'CompositorAnimationWarningHasCurrentColor'
|
||||
} ]);
|
||||
}, 'Background color animations with `current-color` don\'t run on the '
|
||||
+ 'compositor');
|
||||
}
|
||||
|
||||
function start() {
|
||||
var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1']
|
||||
.getService(SpecialPowers.Ci.nsIStringBundleService);
|
||||
@ -1663,6 +1684,7 @@ function start() {
|
||||
testTooLargeFrame();
|
||||
testTransformSVG();
|
||||
testImportantRuleOverride();
|
||||
testCurrentColor();
|
||||
|
||||
promise_test(async t => {
|
||||
var div = addDiv(t, { class: 'compositable',
|
||||
|
22
dom/animation/test/crashtests/1585770.html
Normal file
22
dom/animation/test/crashtests/1585770.html
Normal 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>
|
24
dom/animation/test/crashtests/1604500-1.html
Normal file
24
dom/animation/test/crashtests/1604500-1.html
Normal 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>
|
23
dom/animation/test/crashtests/1611847.html
Normal file
23
dom/animation/test/crashtests/1611847.html
Normal 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>
|
15
dom/animation/test/crashtests/1612891-1.html
Normal file
15
dom/animation/test/crashtests/1612891-1.html
Normal 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>
|
15
dom/animation/test/crashtests/1612891-2.html
Normal file
15
dom/animation/test/crashtests/1612891-2.html
Normal 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>
|
10
dom/animation/test/crashtests/1612891-3.html
Normal file
10
dom/animation/test/crashtests/1612891-3.html
Normal 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>
|
15
dom/animation/test/crashtests/1633442.html
Normal file
15
dom/animation/test/crashtests/1633442.html
Normal 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>
|
20
dom/animation/test/crashtests/1633486.html
Normal file
20
dom/animation/test/crashtests/1633486.html
Normal 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>
|
@ -44,3 +44,11 @@ pref(dom.animations-api.implicit-keyframes.enabled,true) load 1468294-1.html
|
||||
pref(dom.animations-api.implicit-keyframes.enabled,true) load 1467277-1.html
|
||||
load 1524480-1.html
|
||||
load 1575926.html
|
||||
pref(dom.animations-api.implicit-keyframes.enabled,true) load 1585770.html
|
||||
load 1604500-1.html
|
||||
pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.getAnimations.enabled,true) load 1611847.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1612891-1.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1612891-2.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1612891-3.html
|
||||
pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.getAnimations.enabled,true) load 1633442.html
|
||||
pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.implicit-keyframes.enabled,true) load 1633486.html
|
||||
|
@ -46,6 +46,7 @@ skip-if = (toolkit == 'android' && debug) || (os == 'win' && bits == 64) # Bug 1
|
||||
[mozilla/test_distance_of_transform.html]
|
||||
[mozilla/test_document_timeline_origin_time_range.html]
|
||||
[mozilla/test_hide_and_show.html]
|
||||
[mozilla/test_mainthread_synchronization_pref.html]
|
||||
[mozilla/test_moz_prefixed_properties.html]
|
||||
[mozilla/test_pending_animation_tracker.html]
|
||||
[mozilla/test_restyles.html]
|
||||
|
@ -117,6 +117,21 @@ test(t => {
|
||||
);
|
||||
}, 'composite member is ignored on keyframes when using object notation');
|
||||
|
||||
test(t => {
|
||||
const anim = addDiv(t).animate(
|
||||
{ marginLeft: ['0px', '10px'] },
|
||||
100 * MS_PER_SEC
|
||||
);
|
||||
|
||||
for (let frame of anim.effect.getKeyframes()) {
|
||||
assert_false(
|
||||
'composite' in frame,
|
||||
'The BaseComputedKeyframe.composite member is not present'
|
||||
);
|
||||
}
|
||||
}, 'composite member is hidden from the result of ' +
|
||||
'KeyframeEffect::getKeyframes()');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
|
@ -15,11 +15,6 @@ test(t => {
|
||||
}, 'Document.getAnimations() is not available when getAnimations pref is'
|
||||
+ ' disabled');
|
||||
|
||||
test(t => {
|
||||
assert_false('CSSPseudoElement' in window);
|
||||
}, 'CSSPseudoElement interface is not available when getAnimations pref is'
|
||||
+ ' disabled');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
|
@ -1131,6 +1131,19 @@ waitForAllPaints(() => {
|
||||
}
|
||||
);
|
||||
|
||||
add_task_if_omta_enabled(
|
||||
async function animation_visibility_and_opacity() {
|
||||
const div = addDiv(null);
|
||||
const animation1 = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
|
||||
const animation2 = div.animate({ visibility: ['hidden', 'visible'] }, 100 * MS_PER_SEC);
|
||||
await waitForAnimationReadyToRestyle(animation1);
|
||||
await waitForAnimationReadyToRestyle(animation2);
|
||||
const markers = await observeStyling(5);
|
||||
is(markers.length, 5, 'The animation should not be throttled');
|
||||
await ensureElementRemoval(div);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(async function restyling_for_animation_on_orphaned_element() {
|
||||
const div = addDiv(null);
|
||||
const animation = div.animate({ marginLeft: [ '0px', '100px' ] },
|
||||
@ -1956,6 +1969,90 @@ waitForAllPaints(() => {
|
||||
|
||||
await ensureElementRemoval(div);
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(async function no_restyling_animations_in_opacity_zero_element() {
|
||||
const div = addDiv(null, { style: 'animation: on-main-thread 100s infinite; opacity: 0' });
|
||||
const animation = div.getAnimations()[0];
|
||||
|
||||
await waitForAnimationReadyToRestyle(animation);
|
||||
const markers = await observeStyling(5);
|
||||
is(markers.length, 0,
|
||||
'Animations running on the main thread in opacity: 0 element ' +
|
||||
'should never cause restyles');
|
||||
await ensureElementRemoval(div);
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(async function no_restyling_compositor_animations_in_opacity_zero_descendant() {
|
||||
const container = addDiv(null, { style: 'opacity: 0' });
|
||||
const child = addDiv(null, { style: 'animation: rotate 100s infinite;' });
|
||||
container.appendChild(child);
|
||||
|
||||
const animation = child.getAnimations()[0];
|
||||
await waitForAnimationReadyToRestyle(animation);
|
||||
ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
|
||||
|
||||
const markers = await observeStyling(5);
|
||||
|
||||
is(markers.length, 0,
|
||||
'Animations running on the compositor in opacity zero descendant element ' +
|
||||
'should never cause restyles');
|
||||
await ensureElementRemoval(container);
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(async function no_restyling_compositor_animations_in_opacity_zero_descendant_abspos() {
|
||||
const container = addDiv(null, { style: 'opacity: 0' });
|
||||
const child = addDiv(null, { style: 'position: abspos; animation: rotate 100s infinite;' });
|
||||
container.appendChild(child);
|
||||
|
||||
const animation = child.getAnimations()[0];
|
||||
await waitForAnimationReadyToRestyle(animation);
|
||||
ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
|
||||
|
||||
const markers = await observeStyling(5);
|
||||
|
||||
is(markers.length, 0,
|
||||
'Animations running on the compositor in opacity zero abspos descendant element ' +
|
||||
'should never cause restyles');
|
||||
await ensureElementRemoval(container);
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(async function restyling_main_thread_animations_in_opacity_zero_descendant_after_root_opacity_animation() {
|
||||
const container = addDiv(null, { style: 'opacity: 0' });
|
||||
|
||||
const child = addDiv(null, { style: 'animation: on-main-thread 100s infinite;' });
|
||||
container.appendChild(child);
|
||||
|
||||
// Animate the container from 1 to zero opacity and ensure the child animation is throttled then.
|
||||
const containerAnimation = container.animate({ opacity: [ '1', '0' ] }, 100);
|
||||
await containerAnimation.finished;
|
||||
|
||||
const animation = child.getAnimations()[0];
|
||||
await waitForAnimationReadyToRestyle(animation);
|
||||
|
||||
const markers = await observeStyling(5);
|
||||
|
||||
is(markers.length, 0,
|
||||
'Animations running on the compositor in opacity zero descendant element ' +
|
||||
'should never cause restyles after root animation has finished');
|
||||
await ensureElementRemoval(container);
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(async function restyling_main_thread_animations_in_opacity_zero_descendant_during_root_opacity_animation() {
|
||||
const container = addDiv(null, { style: 'opacity: 0; animation: opacity 100s infinite' });
|
||||
|
||||
const child = addDiv(null, { style: 'animation: on-main-thread 100s infinite;' });
|
||||
container.appendChild(child);
|
||||
|
||||
const animation = child.getAnimations()[0];
|
||||
await waitForAnimationReadyToRestyle(animation);
|
||||
|
||||
const markers = await observeStyling(5);
|
||||
|
||||
is(markers.length, 5,
|
||||
'Animations in opacity zero descendant element ' +
|
||||
'should not be throttled if root is animating opacity');
|
||||
await ensureElementRemoval(container);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@ -105,7 +105,7 @@ test(function(t) {
|
||||
div.style.transition = 'margin-left 100s cubic-bezier(0, 1e+39, 0, 0)';
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '0px';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
|
||||
'cubic-bezier(0, ' + max_float + ', 0, 0)',
|
||||
'y1 control point for CSS transition on upper boundary');
|
||||
div.style.transition = '';
|
||||
@ -114,7 +114,7 @@ test(function(t) {
|
||||
div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, 1e+39)';
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '0px';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
|
||||
'cubic-bezier(0, 0, 0, ' + max_float + ')',
|
||||
'y2 control point for CSS transition on upper boundary');
|
||||
div.style.transition = '';
|
||||
@ -123,7 +123,7 @@ test(function(t) {
|
||||
div.style.transition = 'margin-left 100s cubic-bezier(0, -1e+39, 0, 0)';
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '0px';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
|
||||
'cubic-bezier(0, ' + '-' + max_float + ', 0, 0)',
|
||||
'y1 control point for CSS transition on lower boundary');
|
||||
div.style.transition = '';
|
||||
@ -132,7 +132,7 @@ test(function(t) {
|
||||
div.style.transition = 'margin-left 100s cubic-bezier(0, 0, 0, -1e+39)';
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '0px';
|
||||
assert_equals(div.getAnimations()[0].effect.getKeyframes()[0].easing,
|
||||
assert_equals(div.getAnimations()[0].effect.getTiming().easing,
|
||||
'cubic-bezier(0, 0, 0, ' + '-' + max_float + ')',
|
||||
'y2 control point for CSS transition on lower boundary');
|
||||
|
||||
|
@ -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>
|
@ -719,11 +719,10 @@ void AudioChannelService::AudioChannelWindow::AppendAgent(
|
||||
|
||||
RequestAudioFocus(aAgent);
|
||||
AppendAgentAndIncreaseAgentsNum(aAgent);
|
||||
if (aAudible == AudibleState::eAudible) {
|
||||
AudioAudibleChanged(aAgent, AudibleState::eAudible,
|
||||
AudibleChangedReasons::eDataAudibleChanged);
|
||||
} else if (IsEnableAudioCompetingForAllAgents() &&
|
||||
aAudible != AudibleState::eAudible) {
|
||||
AudioAudibleChanged(aAgent, aAudible,
|
||||
AudibleChangedReasons::eDataAudibleChanged);
|
||||
if (IsEnableAudioCompetingForAllAgents() &&
|
||||
aAudible != AudibleState::eAudible) {
|
||||
NotifyAudioCompetingChanged(aAgent);
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,41 @@ class AudioChannelService final : public nsIObserver {
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
/**
|
||||
* We use `AudibleState` to represent the audible state of an owner of audio
|
||||
* channel agent. Those information in AudioChannelWindow could help us to
|
||||
* determine if a tab is being audible or not, in order to tell Chrome JS to
|
||||
* show the sound indicator or delayed autoplay icon on the tab bar.
|
||||
*
|
||||
* - Sound indicator
|
||||
* When a tab is playing sound, we would show the sound indicator on tab bar
|
||||
* to tell users that this tab is producing sound now. In addition, the sound
|
||||
* indicator also give users an ablility to mute or unmute tab.
|
||||
*
|
||||
* When an AudioChannelWindow first contains an agent with state `eAudible`,
|
||||
* or an AudioChannelWindow losts its last agent with state `eAudible`, we
|
||||
* would notify Chrome JS about those changes, to tell them that a tab has
|
||||
* been being audible or not, in order to display or remove the indicator for
|
||||
* a corresponding tab.
|
||||
*
|
||||
* - Delayed autoplay icon (Play Tab icon)
|
||||
* When we enable delaying autoplay, which is to postpone the autoplay media
|
||||
* for unvisited tab until it first goes to foreground, or user click the
|
||||
* play tab icon to resume the delayed media.
|
||||
*
|
||||
* When an AudioChannelWindow first contains an agent with state `eAudible` or
|
||||
* `eMaybeAudible`, we would notify Chrome JS about this change, in order to
|
||||
* show the delayed autoplay tab icon to user, which is used to notice user
|
||||
* there is a media being delayed starting, and then user can click the play
|
||||
* tab icon to resume the start of media, or visit that tab to resume delayed
|
||||
* media automatically.
|
||||
*
|
||||
* According to our UX design, we don't show this icon for inaudible media.
|
||||
* The reason of showing the icon for a tab, where the agent starts with state
|
||||
* `eMaybeAudible`, is because some video might be silent in the beginning
|
||||
* but would soon become audible later.
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
* eNotAudible : agent is not audible
|
||||
* eMaybeAudible : agent is not audible now, but it might be audible later
|
||||
* eAudible : agent is audible now
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsRange.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -42,6 +43,10 @@ template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RawRangeBoundary& aStartBoundary,
|
||||
const RawRangeBoundary& aEndBoundary, StaticRange* aRange);
|
||||
template bool AbstractRange::MaybeCacheToReuse(nsRange& aInstance);
|
||||
template bool AbstractRange::MaybeCacheToReuse(StaticRange& aInstance);
|
||||
|
||||
bool AbstractRange::sHasShutDown = false;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractRange)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange)
|
||||
@ -68,12 +73,56 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AbstractRange)
|
||||
|
||||
// NOTE: If you need to change default value of members of AbstractRange,
|
||||
// update nsRange::Create(nsINode* aNode) and ClearForReuse() too.
|
||||
AbstractRange::AbstractRange(nsINode* aNode)
|
||||
: mIsPositioned(false), mIsGenerated(false), mCalledByJS(false) {
|
||||
Init(aNode);
|
||||
}
|
||||
|
||||
void AbstractRange::Init(nsINode* aNode) {
|
||||
MOZ_ASSERT(aNode, "range isn't in a document!");
|
||||
mOwner = aNode->OwnerDoc();
|
||||
}
|
||||
|
||||
// static
|
||||
void AbstractRange::Shutdown() {
|
||||
sHasShutDown = true;
|
||||
if (nsTArray<RefPtr<nsRange>>* cachedRanges = nsRange::sCachedRanges) {
|
||||
nsRange::sCachedRanges = nullptr;
|
||||
cachedRanges->Clear();
|
||||
delete cachedRanges;
|
||||
}
|
||||
if (nsTArray<RefPtr<StaticRange>>* cachedRanges =
|
||||
StaticRange::sCachedRanges) {
|
||||
StaticRange::sCachedRanges = nullptr;
|
||||
cachedRanges->Clear();
|
||||
delete cachedRanges;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
template <class RangeType>
|
||||
bool AbstractRange::MaybeCacheToReuse(RangeType& aInstance) {
|
||||
static const size_t kMaxRangeCache = 64;
|
||||
|
||||
// If the instance is not used by JS and the cache is not yet full, we
|
||||
// should reuse it. Otherwise, delete it.
|
||||
if (sHasShutDown || aInstance.GetWrapperMaybeDead() || aInstance.GetFlags() ||
|
||||
(RangeType::sCachedRanges &&
|
||||
RangeType::sCachedRanges->Length() == kMaxRangeCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aInstance.ClearForReuse();
|
||||
|
||||
if (!RangeType::sCachedRanges) {
|
||||
RangeType::sCachedRanges = new nsTArray<RefPtr<RangeType>>(16);
|
||||
}
|
||||
RangeType::sCachedRanges->AppendElement(&aInstance);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsINode* AbstractRange::GetClosestCommonInclusiveAncestor() const {
|
||||
return mIsPositioned ? nsContentUtils::GetClosestCommonInclusiveAncestor(
|
||||
mStart.Container(), mEnd.Container())
|
||||
|
@ -23,6 +23,11 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
|
||||
AbstractRange() = delete;
|
||||
explicit AbstractRange(const AbstractRange& aOther) = delete;
|
||||
|
||||
/**
|
||||
* Called when the process is shutting down.
|
||||
*/
|
||||
static void Shutdown();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbstractRange)
|
||||
|
||||
@ -83,6 +88,22 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
|
||||
const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
|
||||
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange);
|
||||
|
||||
template <class RangeType>
|
||||
static bool MaybeCacheToReuse(RangeType& aInstance);
|
||||
|
||||
void Init(nsINode* aNode);
|
||||
|
||||
private:
|
||||
void ClearForReuse() {
|
||||
mOwner = nullptr;
|
||||
mStart = RangeBoundary();
|
||||
mEnd = RangeBoundary();
|
||||
mIsPositioned = false;
|
||||
mIsGenerated = false;
|
||||
mCalledByJS = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
RefPtr<Document> mOwner;
|
||||
RangeBoundary mStart;
|
||||
RangeBoundary mEnd;
|
||||
@ -94,6 +115,8 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
|
||||
bool mIsGenerated;
|
||||
// Used by nsRange, but this should have this for minimizing the size.
|
||||
bool mCalledByJS;
|
||||
|
||||
static bool sHasShutDown;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
68
dom/base/AncestorIterator.h
Normal file
68
dom/base/AncestorIterator.h
Normal 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
|
@ -91,7 +91,12 @@ NS_INTERFACE_TABLE_HEAD(Attr)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Attr)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Attr, LastRelease())
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(Attr,
|
||||
LastRelease(),
|
||||
Destroy())
|
||||
|
||||
NS_IMPL_DOMARENA_DESTROY(Attr)
|
||||
|
||||
void Attr::SetMap(nsDOMAttributeMap* aMap) {
|
||||
if (mAttrMap && !aMap && sInitialized) {
|
||||
@ -116,7 +121,7 @@ nsresult Attr::SetOwnerDocument(Document* aDocument) {
|
||||
|
||||
Document* doc = OwnerDoc();
|
||||
NS_ASSERTION(doc != aDocument, "bad call to Attr::SetOwnerDocument");
|
||||
doc->DeleteAllPropertiesFor(this);
|
||||
doc->RemoveAllPropertiesFor(this);
|
||||
|
||||
RefPtr<dom::NodeInfo> newNodeInfo = aDocument->NodeInfoManager()->GetNodeInfo(
|
||||
mNodeInfo->NameAtom(), mNodeInfo->GetPrefixAtom(),
|
||||
@ -171,8 +176,9 @@ void Attr::SetNodeValueInternal(const nsAString& aNodeValue,
|
||||
nsresult Attr::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
|
||||
nsAutoString value;
|
||||
const_cast<Attr*>(this)->GetValue(value);
|
||||
*aResult = new (aNodeInfo->NodeInfoManager())
|
||||
Attr(nullptr, do_AddRef(aNodeInfo), value);
|
||||
|
||||
*aResult = new Attr(nullptr, do_AddRef(aNodeInfo), value);
|
||||
NS_ADDREF(*aResult);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -26,13 +26,15 @@ class Document;
|
||||
// Attribute helper class used to wrap up an attribute with a dom
|
||||
// object that implements the DOM Attr interface.
|
||||
class Attr final : public nsINode {
|
||||
virtual ~Attr() {}
|
||||
virtual ~Attr() = default;
|
||||
|
||||
public:
|
||||
Attr(nsDOMAttributeMap* aAttrMap, already_AddRefed<dom::NodeInfo>&& aNodeInfo,
|
||||
const nsAString& aValue);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL_DELETECYCLECOLLECTABLE
|
||||
|
||||
NS_DECL_DOMARENA_DESTROY
|
||||
|
||||
NS_IMPL_FROMNODE_HELPER(Attr, IsAttr())
|
||||
|
||||
|
@ -310,6 +310,7 @@ nsresult AttrArray::DoSetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet) {
|
||||
RefPtr<nsMappedAttributes> mapped =
|
||||
GetModifiableMapped(nullptr, nullptr, false);
|
||||
|
||||
mapped->DropStyleSheetReference();
|
||||
mapped->SetStyleSheet(aSheet);
|
||||
|
||||
return MakeMappedUnique(mapped);
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include "mozilla/dom/BarProps.h"
|
||||
#include "mozilla/dom/BarPropBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIScrollable.h"
|
||||
#include "nsIWebBrowserChrome.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -17,7 +17,7 @@ namespace dom {
|
||||
//
|
||||
BarProp::BarProp(nsGlobalWindowInner* aWindow) : mDOMWindow(aWindow) {}
|
||||
|
||||
BarProp::~BarProp() {}
|
||||
BarProp::~BarProp() = default;
|
||||
|
||||
nsPIDOMWindowInner* BarProp::GetParentObject() const { return mDOMWindow; }
|
||||
|
||||
@ -88,7 +88,7 @@ already_AddRefed<nsIWebBrowserChrome> BarProp::GetBrowserChrome() {
|
||||
|
||||
MenubarProp::MenubarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
|
||||
|
||||
MenubarProp::~MenubarProp() {}
|
||||
MenubarProp::~MenubarProp() = default;
|
||||
|
||||
bool MenubarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
|
||||
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_MENUBAR, aRv);
|
||||
@ -106,7 +106,7 @@ void MenubarProp::SetVisible(bool aVisible, CallerType aCallerType,
|
||||
|
||||
ToolbarProp::ToolbarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
|
||||
|
||||
ToolbarProp::~ToolbarProp() {}
|
||||
ToolbarProp::~ToolbarProp() = default;
|
||||
|
||||
bool ToolbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
|
||||
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_TOOLBAR, aRv);
|
||||
@ -125,7 +125,7 @@ void ToolbarProp::SetVisible(bool aVisible, CallerType aCallerType,
|
||||
LocationbarProp::LocationbarProp(nsGlobalWindowInner* aWindow)
|
||||
: BarProp(aWindow) {}
|
||||
|
||||
LocationbarProp::~LocationbarProp() {}
|
||||
LocationbarProp::~LocationbarProp() = default;
|
||||
|
||||
bool LocationbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
|
||||
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_LOCATIONBAR,
|
||||
@ -145,7 +145,7 @@ void LocationbarProp::SetVisible(bool aVisible, CallerType aCallerType,
|
||||
PersonalbarProp::PersonalbarProp(nsGlobalWindowInner* aWindow)
|
||||
: BarProp(aWindow) {}
|
||||
|
||||
PersonalbarProp::~PersonalbarProp() {}
|
||||
PersonalbarProp::~PersonalbarProp() = default;
|
||||
|
||||
bool PersonalbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
|
||||
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR,
|
||||
@ -164,7 +164,7 @@ void PersonalbarProp::SetVisible(bool aVisible, CallerType aCallerType,
|
||||
|
||||
StatusbarProp::StatusbarProp(nsGlobalWindowInner* aWindow) : BarProp(aWindow) {}
|
||||
|
||||
StatusbarProp::~StatusbarProp() {}
|
||||
StatusbarProp::~StatusbarProp() = default;
|
||||
|
||||
bool StatusbarProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
|
||||
return BarProp::GetVisibleByFlag(nsIWebBrowserChrome::CHROME_STATUSBAR, aRv);
|
||||
@ -183,60 +183,24 @@ void StatusbarProp::SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ScrollbarsProp::ScrollbarsProp(nsGlobalWindowInner* aWindow)
|
||||
: BarProp(aWindow) {}
|
||||
|
||||
ScrollbarsProp::~ScrollbarsProp() {}
|
||||
ScrollbarsProp::~ScrollbarsProp() = default;
|
||||
|
||||
bool ScrollbarsProp::GetVisible(CallerType aCallerType, ErrorResult& aRv) {
|
||||
if (!mDOMWindow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScrollable> scroller =
|
||||
do_QueryInterface(mDOMWindow->GetDocShell());
|
||||
|
||||
if (!scroller) {
|
||||
nsIDocShell* ds = mDOMWindow->GetDocShell();
|
||||
if (!ds) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t prefValue;
|
||||
scroller->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
|
||||
&prefValue);
|
||||
if (prefValue != nsIScrollable::Scrollbar_Never) {
|
||||
return true;
|
||||
}
|
||||
|
||||
scroller->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
|
||||
&prefValue);
|
||||
return prefValue != nsIScrollable::Scrollbar_Never;
|
||||
ScrollbarPreference pref = nsDocShell::Cast(ds)->ScrollbarPreference();
|
||||
return pref != ScrollbarPreference::Never;
|
||||
}
|
||||
|
||||
void ScrollbarsProp::SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) {
|
||||
if (aCallerType != CallerType::System) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Scrollbars, unlike the other barprops, implement visibility directly
|
||||
rather than handing off to the superclass (and from there to the
|
||||
chrome window) because scrollbar visibility uniquely applies only
|
||||
to the window making the change (arguably. it does now, anyway.)
|
||||
and because embedding apps have no interface for implementing this
|
||||
themselves, and therefore the implementation must be internal. */
|
||||
|
||||
nsContentUtils::SetScrollbarsVisibility(mDOMWindow->GetDocShell(), aVisible);
|
||||
|
||||
/* Notably absent is the part where we notify the chrome window using
|
||||
GetBrowserChrome()->SetChromeFlags(). Given the possibility of multiple
|
||||
DOM windows (multiple top-level windows, even) within a single
|
||||
chrome window, the historical concept of a single "has scrollbars"
|
||||
flag in the chrome is inapplicable, and we can't tell at this level
|
||||
whether we represent the particular DOM window that makes this decision
|
||||
for the chrome.
|
||||
|
||||
So only this object (and its corresponding DOM window) knows whether
|
||||
scrollbars are visible. The corresponding chrome window will need to
|
||||
ask (one of) its DOM window(s) when it needs to know about scrollbar
|
||||
visibility, rather than caching its own copy of that information.
|
||||
*/
|
||||
void ScrollbarsProp::SetVisible(bool aVisible, CallerType, ErrorResult&) {
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -36,8 +36,7 @@ class BarProp : public nsISupports, public nsWrapperCache {
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
|
||||
|
||||
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) = 0;
|
||||
virtual void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
@ -61,9 +60,9 @@ class MenubarProp final : public BarProp {
|
||||
explicit MenubarProp(nsGlobalWindowInner* aWindow);
|
||||
virtual ~MenubarProp();
|
||||
|
||||
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
virtual void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
};
|
||||
|
||||
// Script "toolbar" object
|
||||
@ -72,9 +71,9 @@ class ToolbarProp final : public BarProp {
|
||||
explicit ToolbarProp(nsGlobalWindowInner* aWindow);
|
||||
virtual ~ToolbarProp();
|
||||
|
||||
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
virtual void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
};
|
||||
|
||||
// Script "locationbar" object
|
||||
@ -83,9 +82,9 @@ class LocationbarProp final : public BarProp {
|
||||
explicit LocationbarProp(nsGlobalWindowInner* aWindow);
|
||||
virtual ~LocationbarProp();
|
||||
|
||||
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
virtual void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
};
|
||||
|
||||
// Script "personalbar" object
|
||||
@ -94,9 +93,9 @@ class PersonalbarProp final : public BarProp {
|
||||
explicit PersonalbarProp(nsGlobalWindowInner* aWindow);
|
||||
virtual ~PersonalbarProp();
|
||||
|
||||
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
virtual void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
};
|
||||
|
||||
// Script "statusbar" object
|
||||
@ -105,9 +104,9 @@ class StatusbarProp final : public BarProp {
|
||||
explicit StatusbarProp(nsGlobalWindowInner* aWindow);
|
||||
virtual ~StatusbarProp();
|
||||
|
||||
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
virtual void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
};
|
||||
|
||||
// Script "scrollbars" object
|
||||
@ -116,9 +115,9 @@ class ScrollbarsProp final : public BarProp {
|
||||
explicit ScrollbarsProp(nsGlobalWindowInner* aWindow);
|
||||
virtual ~ScrollbarsProp();
|
||||
|
||||
virtual bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
virtual void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
bool GetVisible(CallerType aCallerType, ErrorResult& aRv) override;
|
||||
void SetVisible(bool aVisible, CallerType aCallerType,
|
||||
ErrorResult& aRv) override;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -59,10 +59,7 @@ struct MOZ_STACK_CLASS BindContext final {
|
||||
: nullptr),
|
||||
mInComposedDoc(aParent.IsInComposedDoc()),
|
||||
mInUncomposedDoc(aParent.IsInUncomposedDoc()),
|
||||
mSubtreeRootChanges(true),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aParent)) {}
|
||||
mSubtreeRootChanges(true) {}
|
||||
|
||||
// When re-binding a shadow host into a tree, we re-bind all the shadow tree
|
||||
// from the root. In that case, the shadow tree contents remain within the
|
||||
@ -75,10 +72,7 @@ struct MOZ_STACK_CLASS BindContext final {
|
||||
mBindingParent(aShadowRoot.Host()),
|
||||
mInComposedDoc(aShadowRoot.IsInComposedDoc()),
|
||||
mInUncomposedDoc(false),
|
||||
mSubtreeRootChanges(false),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aShadowRoot)) {}
|
||||
mSubtreeRootChanges(false) {}
|
||||
|
||||
// This constructor is meant to be used when inserting native-anonymous
|
||||
// children into a subtree.
|
||||
@ -88,10 +82,7 @@ struct MOZ_STACK_CLASS BindContext final {
|
||||
mBindingParent(&aParentElement),
|
||||
mInComposedDoc(aParentElement.IsInComposedDoc()),
|
||||
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
|
||||
mSubtreeRootChanges(true),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aParentElement)) {
|
||||
mSubtreeRootChanges(true) {
|
||||
MOZ_ASSERT(mInComposedDoc, "Binding NAC in a disconnected subtree?");
|
||||
}
|
||||
|
||||
@ -101,27 +92,13 @@ struct MOZ_STACK_CLASS BindContext final {
|
||||
mBindingParent(aBinding.GetBoundElement()),
|
||||
mInComposedDoc(aParentElement.IsInComposedDoc()),
|
||||
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
|
||||
mSubtreeRootChanges(true),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aParentElement)) {}
|
||||
|
||||
bool CollectingDisplayedNodeDataDuringLoad() const {
|
||||
return mCollectingDisplayedNodeDataDuringLoad;
|
||||
}
|
||||
mSubtreeRootChanges(true) {}
|
||||
|
||||
private:
|
||||
static bool IsLikelyUndisplayed(const nsINode& aParent) {
|
||||
return aParent.IsAnyOfHTMLElements(nsGkAtoms::style, nsGkAtoms::script);
|
||||
}
|
||||
|
||||
static bool ShouldCollectDisplayedNodeDataDuringLoad(bool aConnected,
|
||||
Document& aDoc,
|
||||
nsINode& aParent) {
|
||||
return aDoc.GetReadyStateEnum() == Document::READYSTATE_LOADING &&
|
||||
aConnected && !IsLikelyUndisplayed(aParent);
|
||||
}
|
||||
|
||||
Document& mDoc;
|
||||
|
||||
Element* const mBindingParent;
|
||||
@ -132,27 +109,6 @@ struct MOZ_STACK_CLASS BindContext final {
|
||||
// Whether the bind operation will change the subtree root of the content
|
||||
// we're binding.
|
||||
const bool mSubtreeRootChanges;
|
||||
|
||||
// Whether it's likely that we're in an undisplayed part of the DOM.
|
||||
//
|
||||
// NOTE(emilio): We don't inherit this in BindContext's for Shadow DOM or XBL
|
||||
// or such. This means that if you have a shadow tree inside an undisplayed
|
||||
// element it will be incorrectly counted. But given our current definition
|
||||
// of undisplayed element this is not likely to matter in practice.
|
||||
bool mCollectingDisplayedNodeDataDuringLoad;
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS BindContext::NestingLevel {
|
||||
explicit NestingLevel(BindContext& aContext, const Element& aParent)
|
||||
: mRestoreCollecting(aContext.mCollectingDisplayedNodeDataDuringLoad) {
|
||||
if (aContext.mCollectingDisplayedNodeDataDuringLoad) {
|
||||
aContext.mCollectingDisplayedNodeDataDuringLoad =
|
||||
BindContext::IsLikelyUndisplayed(aParent);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AutoRestore<bool> mRestoreCollecting;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -735,7 +735,7 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
|
||||
|
||||
error.WouldReportJSException();
|
||||
if (error.Failed()) {
|
||||
localPromise->MaybeReject(error);
|
||||
localPromise->MaybeReject(std::move(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,7 +318,7 @@ BodyStream::BodyStream(nsIGlobalObject* aGlobal,
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
|
||||
}
|
||||
|
||||
BodyStream::~BodyStream() {}
|
||||
BodyStream::~BodyStream() = default;
|
||||
|
||||
void BodyStream::ErrorPropagation(JSContext* aCx,
|
||||
const MutexAutoLock& aProofOfLock,
|
||||
@ -337,10 +337,16 @@ void BodyStream::ErrorPropagation(JSContext* aCx,
|
||||
}
|
||||
|
||||
// Let's use a generic error.
|
||||
RefPtr<DOMException> error = DOMException::Create(NS_ERROR_DOM_TYPE_ERR);
|
||||
ErrorResult rv;
|
||||
// XXXbz can we come up with a better error message here to tell the
|
||||
// consumer what went wrong?
|
||||
rv.ThrowTypeError("Error in body stream");
|
||||
|
||||
JS::Rooted<JS::Value> errorValue(aCx);
|
||||
if (ToJSValue(aCx, error, &errorValue)) {
|
||||
bool ok = ToJSValue(aCx, std::move(rv), &errorValue);
|
||||
MOZ_RELEASE_ASSERT(ok, "ToJSValue never fails for ErrorResult");
|
||||
|
||||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
JS::ReadableStreamError(aCx, aStream, errorValue);
|
||||
}
|
||||
|
@ -458,13 +458,8 @@ nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||
aParent.AsContent()->GetContainingShadow();
|
||||
}
|
||||
|
||||
if (IsInComposedDoc()) {
|
||||
if (mText.IsBidi()) {
|
||||
aContext.OwnerDoc().SetBidiEnabled();
|
||||
}
|
||||
if (aContext.CollectingDisplayedNodeDataDuringLoad()) {
|
||||
aContext.OwnerDoc().AddToVisibleContentHeuristic(mText.GetLength());
|
||||
}
|
||||
if (IsInComposedDoc() && mText.IsBidi()) {
|
||||
aContext.OwnerDoc().SetBidiEnabled();
|
||||
}
|
||||
|
||||
// Clear the lazy frame construction bits.
|
||||
|
@ -310,7 +310,7 @@ class MOZ_NEEDS_MEMMOVABLE_MEMBERS StyleChildrenIterator
|
||||
return *this;
|
||||
}
|
||||
|
||||
~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
|
||||
MOZ_COUNTED_DTOR(StyleChildrenIterator)
|
||||
|
||||
using AllChildrenIterator::GetNextChild;
|
||||
using AllChildrenIterator::GetPreviousChild;
|
||||
|
@ -25,7 +25,7 @@ void ChromeNodeList::Append(nsINode& aNode, ErrorResult& aError) {
|
||||
if (!aNode.IsContent()) {
|
||||
// nsINodeList deals with nsIContent objects only, so need to
|
||||
// filter out other nodes for now.
|
||||
aError.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
aError.ThrowTypeError("The node passed in is not a ChildNode");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ void ChromeNodeList::Append(nsINode& aNode, ErrorResult& aError) {
|
||||
|
||||
void ChromeNodeList::Remove(nsINode& aNode, ErrorResult& aError) {
|
||||
if (!aNode.IsContent()) {
|
||||
aError.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
aError.ThrowTypeError("The node passed in is not a ChildNode");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,12 +83,12 @@ void ChromeUtils::Base64URLEncode(GlobalObject& aGlobal,
|
||||
uint8_t* data = nullptr;
|
||||
if (aSource.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aSource.GetAsArrayBuffer();
|
||||
buffer.ComputeLengthAndData();
|
||||
buffer.ComputeState();
|
||||
length = buffer.Length();
|
||||
data = buffer.Data();
|
||||
} else if (aSource.IsArrayBufferView()) {
|
||||
const ArrayBufferView& view = aSource.GetAsArrayBufferView();
|
||||
view.ComputeLengthAndData();
|
||||
view.ComputeState();
|
||||
length = view.Length();
|
||||
data = view.Data();
|
||||
} else {
|
||||
@ -774,7 +774,7 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
||||
thread->mCpuKernel = entry.cpuKernel;
|
||||
thread->mTid = entry.tid;
|
||||
}
|
||||
procInfo.mThreads = threads;
|
||||
procInfo.mThreads = std::move(threads);
|
||||
|
||||
mozilla::dom::Sequence<mozilla::dom::ChildProcInfoDictionary>
|
||||
children;
|
||||
@ -810,9 +810,9 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
||||
thread->mTid = entry.tid;
|
||||
thread->mName.Assign(entry.name);
|
||||
}
|
||||
childProcInfo->mThreads = threads;
|
||||
childProcInfo->mThreads = std::move(threads);
|
||||
}
|
||||
procInfo.mChildren = children;
|
||||
procInfo.mChildren = std::move(children);
|
||||
domPromise->MaybeResolve(procInfo);
|
||||
}; // end of ProcInfoResolver
|
||||
|
||||
|
@ -17,12 +17,13 @@ using namespace dom;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
Comment::~Comment() {}
|
||||
Comment::~Comment() = default;
|
||||
|
||||
already_AddRefed<CharacterData> Comment::CloneDataNode(
|
||||
mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const {
|
||||
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
|
||||
RefPtr<Comment> it = new Comment(ni.forget());
|
||||
auto* nim = ni->NodeInfoManager();
|
||||
RefPtr<Comment> it = new (nim) Comment(ni.forget());
|
||||
if (aCloneText) {
|
||||
it->mText = mText;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Crypto, mParent, mSubtle)
|
||||
|
||||
Crypto::Crypto(nsIGlobalObject* aParent) : mParent(aParent) {}
|
||||
|
||||
Crypto::~Crypto() {}
|
||||
Crypto::~Crypto() = default;
|
||||
|
||||
/* virtual */
|
||||
JSObject* Crypto::WrapObject(JSContext* aCx,
|
||||
@ -58,7 +58,7 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
||||
return;
|
||||
}
|
||||
|
||||
aArray.ComputeLengthAndData();
|
||||
aArray.ComputeState();
|
||||
uint32_t dataLen = aArray.Length();
|
||||
if (dataLen == 0) {
|
||||
NS_WARNING("ArrayBufferView length is 0, cannot continue");
|
||||
|
@ -656,8 +656,7 @@ bool CustomElementRegistry::JSObjectToAtomArray(
|
||||
|
||||
if (!iterable.isUndefined()) {
|
||||
if (!iterable.isObject()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
|
||||
NS_LITERAL_STRING("CustomElementRegistry.define: ") + aName);
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_ConvertUTF16toUTF8(aName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -668,8 +667,7 @@ bool CustomElementRegistry::JSObjectToAtomArray(
|
||||
}
|
||||
|
||||
if (!iter.valueIsIterable()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
|
||||
NS_LITERAL_STRING("CustomElementRegistry.define: ") + aName);
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_ConvertUTF16toUTF8(aName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -691,10 +689,9 @@ bool CustomElementRegistry::JSObjectToAtomArray(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aArray.AppendElement(NS_Atomize(attrStr))) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
// XXX(Bug 1631371) Check if this should use a fallible operation as it
|
||||
// pretended earlier.
|
||||
aArray.AppendElement(NS_Atomize(attrStr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -728,8 +725,7 @@ void CustomElementRegistry::Define(
|
||||
* these steps.
|
||||
*/
|
||||
if (!JS::IsConstructor(constructorUnwrapped)) {
|
||||
aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(
|
||||
NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define"));
|
||||
aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>("Argument 2");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -868,8 +864,7 @@ void CustomElementRegistry::Define(
|
||||
* 14.2. If Type(prototype) is not Object, then throw a TypeError exception.
|
||||
*/
|
||||
if (!prototype.isObject()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING(
|
||||
"CustomElementRegistry.define: constructor.prototype"));
|
||||
aRv.ThrowTypeError<MSG_NOT_OBJECT>("constructor.prototype");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -961,7 +956,7 @@ void CustomElementRegistry::Define(
|
||||
disableInternals, disableShadow);
|
||||
|
||||
CustomElementDefinition* def = definition.get();
|
||||
mCustomDefinitions.Put(nameAtom, definition.forget());
|
||||
mCustomDefinitions.Put(nameAtom, std::move(definition));
|
||||
|
||||
MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
|
||||
"Number of entries should be the same");
|
||||
@ -1020,7 +1015,7 @@ void CustomElementRegistry::SetElementCreationCallback(
|
||||
}
|
||||
|
||||
RefPtr<CustomElementCreationCallback> callback = &aCallback;
|
||||
mElementCreationCallbacks.Put(nameAtom, callback.forget());
|
||||
mElementCreationCallbacks.Put(nameAtom, std::move(callback));
|
||||
}
|
||||
|
||||
void CustomElementRegistry::Upgrade(nsINode& aRoot) {
|
||||
@ -1119,7 +1114,7 @@ static void DoUpgrade(Element* aElement, CustomElementDefinition* aDefinition,
|
||||
// always forms the return value from a JSObject.
|
||||
if (NS_FAILED(UNWRAP_OBJECT(Element, &constructResult, element)) ||
|
||||
element != aElement) {
|
||||
aRv.ThrowTypeError(u"Custom element constructor returned a wrong element");
|
||||
aRv.ThrowTypeError("Custom element constructor returned a wrong element");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ struct CustomElementData {
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~CustomElementData() {}
|
||||
virtual ~CustomElementData() = default;
|
||||
|
||||
// Custom element type, for <button is="x-button"> or <x-button>
|
||||
// this would be x-button.
|
||||
@ -193,7 +193,7 @@ struct CustomElementDefinition {
|
||||
}
|
||||
|
||||
private:
|
||||
~CustomElementDefinition() {}
|
||||
~CustomElementDefinition() = default;
|
||||
};
|
||||
|
||||
class CustomElementReaction {
|
||||
@ -290,7 +290,8 @@ class CustomElementReactionsStack {
|
||||
}
|
||||
|
||||
private:
|
||||
~CustomElementReactionsStack(){};
|
||||
~CustomElementReactionsStack() = default;
|
||||
;
|
||||
|
||||
/**
|
||||
* Push a new element queue onto the custom element reactions stack.
|
||||
|
52
dom/base/DOMArena.h
Normal file
52
dom/base/DOMArena.h
Normal 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
|
@ -168,7 +168,7 @@ class DOMException : public Exception {
|
||||
const nsACString& aMessage);
|
||||
|
||||
protected:
|
||||
virtual ~DOMException() {}
|
||||
virtual ~DOMException() = default;
|
||||
|
||||
uint16_t mCode;
|
||||
};
|
||||
|
@ -158,7 +158,8 @@ nsresult DOMImplementation::CreateHTMLDocument(const nsAString& aTitle,
|
||||
rv = head->AppendChildTo(title, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<nsTextNode> titleText = new nsTextNode(doc->NodeInfoManager());
|
||||
RefPtr<nsTextNode> titleText =
|
||||
new (doc->NodeInfoManager()) nsTextNode(doc->NodeInfoManager());
|
||||
rv = titleText->SetText(aTitle, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = title->AppendChildTo(titleText, false);
|
||||
|
@ -23,7 +23,7 @@ class Document;
|
||||
class DocumentType;
|
||||
|
||||
class DOMImplementation final : public nsISupports, public nsWrapperCache {
|
||||
~DOMImplementation() {}
|
||||
~DOMImplementation() = default;
|
||||
|
||||
public:
|
||||
DOMImplementation(Document* aOwner, nsIGlobalObject* aScriptObject,
|
||||
|
@ -90,7 +90,7 @@ already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
|
||||
observer->mThresholds.SetCapacity(thresholds.Length());
|
||||
for (const auto& thresh : thresholds) {
|
||||
if (thresh < 0.0 || thresh > 1.0) {
|
||||
aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
|
||||
aRv.ThrowRangeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
observer->mThresholds.AppendElement(thresh);
|
||||
@ -99,7 +99,7 @@ already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
|
||||
} else {
|
||||
double thresh = aOptions.mThreshold.GetAsDouble();
|
||||
if (thresh < 0.0 || thresh > 1.0) {
|
||||
aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
|
||||
aRv.ThrowRangeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
observer->mThresholds.AppendElement(thresh);
|
||||
|
@ -17,7 +17,7 @@ class DOMIntersectionObserver;
|
||||
|
||||
class DOMIntersectionObserverEntry final : public nsISupports,
|
||||
public nsWrapperCache {
|
||||
~DOMIntersectionObserverEntry() {}
|
||||
~DOMIntersectionObserverEntry() = default;
|
||||
|
||||
public:
|
||||
DOMIntersectionObserverEntry(nsISupports* aOwner, DOMHighResTimeStamp aTime,
|
||||
|
@ -58,8 +58,7 @@ static bool ValidateAndFixupMatrix2DInit(DOMMatrix2DInit& aMatrixInit,
|
||||
} \
|
||||
}
|
||||
#define ValidateAndSet(field, alias, fieldName, aliasName, defaultValue) \
|
||||
ValidateAliases((field), (alias), NS_LITERAL_STRING(fieldName), \
|
||||
NS_LITERAL_STRING(aliasName)); \
|
||||
ValidateAliases((field), (alias), fieldName, aliasName); \
|
||||
SetFromAliasOrDefault((field), (alias), (defaultValue));
|
||||
|
||||
ValidateAndSet(aMatrixInit.mM11, aMatrixInit.mA, "m11", "a", 1);
|
||||
@ -79,17 +78,16 @@ static bool ValidateAndFixupMatrix2DInit(DOMMatrix2DInit& aMatrixInit,
|
||||
// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
|
||||
static bool ValidateAndFixupMatrixInit(DOMMatrixInit& aMatrixInit,
|
||||
ErrorResult& aRv) {
|
||||
#define Check3DField(field, fieldName, defaultValue) \
|
||||
if ((field) != (defaultValue)) { \
|
||||
if (!aMatrixInit.mIs2D.WasPassed()) { \
|
||||
aMatrixInit.mIs2D.Construct(false); \
|
||||
return true; \
|
||||
} \
|
||||
if (aMatrixInit.mIs2D.Value()) { \
|
||||
aRv.ThrowTypeError<MSG_MATRIX_INIT_EXCEEDS_2D>( \
|
||||
NS_LITERAL_STRING(fieldName)); \
|
||||
return false; \
|
||||
} \
|
||||
#define Check3DField(field, fieldName, defaultValue) \
|
||||
if ((field) != (defaultValue)) { \
|
||||
if (!aMatrixInit.mIs2D.WasPassed()) { \
|
||||
aMatrixInit.mIs2D.Construct(false); \
|
||||
return true; \
|
||||
} \
|
||||
if (aMatrixInit.mIs2D.Value()) { \
|
||||
aRv.ThrowTypeError<MSG_MATRIX_INIT_EXCEEDS_2D>(fieldName); \
|
||||
return false; \
|
||||
} \
|
||||
}
|
||||
|
||||
if (!ValidateAndFixupMatrix2DInit(aMatrixInit, aRv)) {
|
||||
@ -190,7 +188,7 @@ already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromMatrix(
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv) {
|
||||
aArray32.ComputeLengthAndData();
|
||||
aArray32.ComputeState();
|
||||
|
||||
const int length = aArray32.Length();
|
||||
const bool is2D = length == 6;
|
||||
@ -204,7 +202,7 @@ already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv) {
|
||||
aArray64.ComputeLengthAndData();
|
||||
aArray64.ComputeState();
|
||||
|
||||
const int length = aArray64.Length();
|
||||
const bool is2D = length == 6;
|
||||
@ -637,7 +635,7 @@ already_AddRefed<DOMMatrix> DOMMatrix::FromMatrix(
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv) {
|
||||
aArray32.ComputeLengthAndData();
|
||||
aArray32.ComputeState();
|
||||
|
||||
const int length = aArray32.Length();
|
||||
const bool is2D = length == 6;
|
||||
@ -650,7 +648,7 @@ already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv) {
|
||||
aArray64.ComputeLengthAndData();
|
||||
aArray64.ComputeState();
|
||||
|
||||
const int length = aArray64.Length();
|
||||
const bool is2D = length == 6;
|
||||
@ -724,7 +722,7 @@ static void SetDataInMatrix(DOMMatrixReadOnly* aMatrix, const T* aData,
|
||||
aMatrix->SetE(aData[4]);
|
||||
aMatrix->SetF(aData[5]);
|
||||
} else {
|
||||
nsAutoString lengthStr;
|
||||
nsAutoCString lengthStr;
|
||||
lengthStr.AppendInt(aLength);
|
||||
aRv.ThrowTypeError<MSG_MATRIX_INIT_LENGTH_WRONG>(lengthStr);
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ class DOMMatrixReadOnly : public nsWrapperCache {
|
||||
nsAutoPtr<gfx::MatrixDouble> mMatrix2D;
|
||||
nsAutoPtr<gfx::Matrix4x4Double> mMatrix3D;
|
||||
|
||||
virtual ~DOMMatrixReadOnly() {}
|
||||
virtual ~DOMMatrixReadOnly() = default;
|
||||
|
||||
/**
|
||||
* Sets data from a fully validated and fixed-up matrix init,
|
||||
@ -317,7 +317,7 @@ class DOMMatrix : public DOMMatrixReadOnly {
|
||||
DOMMatrix* InvertSelf();
|
||||
DOMMatrix* SetMatrixValue(const nsACString&, ErrorResult&);
|
||||
|
||||
virtual ~DOMMatrix() {}
|
||||
virtual ~DOMMatrix() = default;
|
||||
|
||||
private:
|
||||
DOMMatrix(nsISupports* aParent, bool is2D)
|
||||
|
@ -37,7 +37,7 @@ DOMParser::DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal,
|
||||
MOZ_ASSERT(aDocumentURI);
|
||||
}
|
||||
|
||||
DOMParser::~DOMParser() {}
|
||||
DOMParser::~DOMParser() = default;
|
||||
|
||||
// QueryInterface implementation for DOMParser
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser)
|
||||
@ -116,7 +116,7 @@ already_AddRefed<Document> DOMParser::ParseFromSafeString(const nsAString& aStr,
|
||||
already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv) {
|
||||
aBuf.ComputeLengthAndData();
|
||||
aBuf.ComputeState();
|
||||
return ParseFromBuffer(MakeSpan(aBuf.Data(), aBuf.Length()), aType, aRv);
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ class DOMPointReadOnly : public nsWrapperCache {
|
||||
JSStructuredCloneReader* aReader);
|
||||
|
||||
protected:
|
||||
virtual ~DOMPointReadOnly() {}
|
||||
virtual ~DOMPointReadOnly() = default;
|
||||
|
||||
// Shared implementation of ReadStructuredClone for DOMPoint and
|
||||
// DOMPointReadOnly.
|
||||
|
@ -28,7 +28,7 @@ DOMQuad::DOMQuad(nsISupports* aParent, CSSPoint aPoints[4]) : mParent(aParent) {
|
||||
|
||||
DOMQuad::DOMQuad(nsISupports* aParent) : mParent(aParent) {}
|
||||
|
||||
DOMQuad::~DOMQuad() {}
|
||||
DOMQuad::~DOMQuad() = default;
|
||||
|
||||
JSObject* DOMQuad::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
|
@ -25,7 +25,7 @@ struct DOMRectInit;
|
||||
|
||||
class DOMRectReadOnly : public nsISupports, public nsWrapperCache {
|
||||
protected:
|
||||
virtual ~DOMRectReadOnly() {}
|
||||
virtual ~DOMRectReadOnly() = default;
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -129,11 +129,11 @@ class DOMRect final : public DOMRectReadOnly {
|
||||
}
|
||||
|
||||
private:
|
||||
~DOMRect() {}
|
||||
~DOMRect() = default;
|
||||
};
|
||||
|
||||
class DOMRectList final : public nsISupports, public nsWrapperCache {
|
||||
~DOMRectList() {}
|
||||
~DOMRectList() = default;
|
||||
|
||||
public:
|
||||
explicit DOMRectList(nsISupports* aParent) : mParent(aParent) {}
|
||||
|
@ -81,7 +81,7 @@ class DOMRequest : public DOMEventTargetHelper {
|
||||
};
|
||||
|
||||
class DOMRequestService final : public nsIDOMRequestService {
|
||||
~DOMRequestService() {}
|
||||
~DOMRequestService() = default;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -18,7 +18,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMStringList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
DOMStringList::~DOMStringList() {}
|
||||
DOMStringList::~DOMStringList() = default;
|
||||
|
||||
JSObject* DOMStringList::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
|
@ -55,9 +55,11 @@ class DOMStringList : public nsISupports, public nsWrapperCache {
|
||||
}
|
||||
|
||||
bool Add(const nsAString& aName) {
|
||||
// XXXbz mNames should really be a fallible array; otherwise this
|
||||
// return value is meaningless.
|
||||
return mNames.AppendElement(aName) != nullptr;
|
||||
// XXXbz(Bug 1631374) mNames should really be a fallible array; otherwise
|
||||
// this return value is meaningless. return mNames.AppendElement(aName) !=
|
||||
// nullptr;
|
||||
mNames.AppendElement(aName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Clear() { mNames.Clear(); }
|
||||
|
@ -274,11 +274,14 @@ static bool AncestorChainCrossesShadowBoundary(nsIContent* aDescendant,
|
||||
* test for it separately, e.g. with DoesNotAffectDirectionOfAncestors.
|
||||
* It *does* include textarea, because even if a textarea has dir=auto, it has
|
||||
* unicode-bidi: plaintext and is handled automatically in bidi resolution.
|
||||
* It also includes `input`, because it takes the `dir` value from its value
|
||||
* attribute, instead of the child nodes.
|
||||
*/
|
||||
static bool DoesNotParticipateInAutoDirection(const nsIContent* aContent) {
|
||||
mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo();
|
||||
return ((!aContent->IsHTMLElement() || nodeInfo->Equals(nsGkAtoms::script) ||
|
||||
nodeInfo->Equals(nsGkAtoms::style) ||
|
||||
nodeInfo->Equals(nsGkAtoms::input) ||
|
||||
nodeInfo->Equals(nsGkAtoms::textarea) ||
|
||||
aContent->IsInAnonymousSubtree())) &&
|
||||
!aContent->IsShadowRoot();
|
||||
@ -506,9 +509,7 @@ class nsTextNodeDirectionalityMap {
|
||||
aTextNode->SetHasTextNodeDirectionalityMap();
|
||||
}
|
||||
|
||||
~nsTextNodeDirectionalityMap() {
|
||||
MOZ_COUNT_DTOR(nsTextNodeDirectionalityMap);
|
||||
}
|
||||
MOZ_COUNTED_DTOR(nsTextNodeDirectionalityMap)
|
||||
|
||||
static void nsTextNodeDirectionalityMapPropertyDestructor(
|
||||
void* aObject, nsAtom* aProperty, void* aPropertyValue, void* aData) {
|
||||
@ -536,7 +537,7 @@ class nsTextNodeDirectionalityMap {
|
||||
|
||||
mElements.Remove(aElement);
|
||||
aElement->ClearHasDirAutoSet();
|
||||
aElement->DeleteProperty(nsGkAtoms::dirAutoSetBy);
|
||||
aElement->RemoveProperty(nsGkAtoms::dirAutoSetBy);
|
||||
}
|
||||
|
||||
void RemoveEntryForProperty(Element* aElement) {
|
||||
@ -602,7 +603,7 @@ class nsTextNodeDirectionalityMap {
|
||||
nsTextNodeDirectionalityMap::AddEntryToMap(newTextNode, rootNode);
|
||||
} else {
|
||||
rootNode->ClearHasDirAutoSet();
|
||||
rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy);
|
||||
rootNode->RemoveProperty(nsGkAtoms::dirAutoSetBy);
|
||||
}
|
||||
return OpRemove;
|
||||
}
|
||||
@ -631,7 +632,7 @@ class nsTextNodeDirectionalityMap {
|
||||
mElements.EnumerateEntries(TakeEntries, &entries);
|
||||
for (Element* el : entries) {
|
||||
el->ClearHasDirAutoSet();
|
||||
el->DeleteProperty(nsGkAtoms::dirAutoSetBy);
|
||||
el->RemoveProperty(nsGkAtoms::dirAutoSetBy);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,10 @@ DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey)
|
||||
: mKey(aKey), mTabGroup(aTabGroup) {
|
||||
// This method does not add itself to mTabGroup->mDocGroups as the caller does
|
||||
// it for us.
|
||||
if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
|
||||
mArena = new mozilla::dom::DOMArena();
|
||||
}
|
||||
|
||||
mPerformanceCounter =
|
||||
new mozilla::PerformanceCounter(NS_LITERAL_CSTRING("DocGroup:") + aKey);
|
||||
}
|
||||
@ -60,6 +64,8 @@ DocGroup::~DocGroup() {
|
||||
nsIEventTarget* target = EventTargetFor(TaskCategory::Other);
|
||||
NS_ProxyRelease("DocGroup::mReactionsStack", target,
|
||||
mReactionsStack.forget());
|
||||
|
||||
NS_ProxyRelease("DocGroup::mArena", target, mArena.forget());
|
||||
}
|
||||
|
||||
mTabGroup->mDocGroups.RemoveEntry(mKey);
|
||||
@ -144,7 +150,7 @@ RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
|
||||
->Then(
|
||||
mainThread, __func__,
|
||||
[self, host, pid, windowID, duration, isTopLevel,
|
||||
items](const PerformanceMemoryInfo& aMemoryInfo) {
|
||||
items = std::move(items)](const PerformanceMemoryInfo& aMemoryInfo) {
|
||||
PerformanceInfo info =
|
||||
PerformanceInfo(host, pid, windowID, duration,
|
||||
self->mPerformanceCounter->GetID(), false,
|
||||
|
@ -56,6 +56,9 @@ class DocGroup final {
|
||||
RefPtr<PerformanceInfoPromise> ReportPerformanceInfo();
|
||||
|
||||
TabGroup* GetTabGroup() { return mTabGroup; }
|
||||
|
||||
mozilla::dom::DOMArena* ArenaAllocator() { return mArena; }
|
||||
|
||||
mozilla::dom::CustomElementReactionsStack* CustomElementReactionsStack() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mReactionsStack) {
|
||||
@ -124,6 +127,8 @@ class DocGroup final {
|
||||
|
||||
RefPtr<mozilla::ThrottledEventQueue> mIframePostMessageQueue;
|
||||
nsTHashtable<nsUint64HashKey> mIframesUsedPostMessageQueue;
|
||||
|
||||
RefPtr<mozilla::dom::DOMArena> mArena;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,15 @@
|
||||
#include "mozilla/EventStates.h" // for EventStates
|
||||
#include "mozilla/FlushType.h" // for enum
|
||||
#include "mozilla/MozPromise.h" // for MozPromise
|
||||
#include "mozilla/FunctionRef.h" // for FunctionRef
|
||||
#include "mozilla/Pair.h" // for Pair
|
||||
#include "nsAutoPtr.h" // for member
|
||||
#include "nsCOMArray.h" // for member
|
||||
#include "nsCompatibility.h" // for member
|
||||
#include "nsCOMPtr.h" // for member
|
||||
#include "nsICookieSettings.h"
|
||||
#include "nsGkAtoms.h" // for static class members
|
||||
#include "nsGkAtoms.h" // for static class members
|
||||
#include "nsNameSpaceManager.h" // for static class members
|
||||
#include "nsIApplicationCache.h"
|
||||
#include "nsIApplicationCacheContainer.h"
|
||||
#include "nsIContentViewer.h"
|
||||
@ -144,6 +146,7 @@ class ServoStyleSet;
|
||||
enum class StyleOrigin : uint8_t;
|
||||
class SMILAnimationController;
|
||||
enum class StyleCursorKind : uint8_t;
|
||||
enum class StylePrefersColorScheme : uint8_t;
|
||||
template <typename>
|
||||
class OwningNonNull;
|
||||
struct URLExtraData;
|
||||
@ -284,7 +287,7 @@ class DocHeaderData {
|
||||
};
|
||||
|
||||
class ExternalResourceMap {
|
||||
typedef bool (*SubDocEnumFunc)(Document& aDocument, void* aData);
|
||||
using SubDocEnumFunc = FunctionRef<bool(Document&)>;
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -327,7 +330,7 @@ class ExternalResourceMap {
|
||||
* Enumerate the resource documents. See
|
||||
* Document::EnumerateExternalResources.
|
||||
*/
|
||||
void EnumerateResources(SubDocEnumFunc aCallback, void* aData);
|
||||
void EnumerateResources(SubDocEnumFunc aCallback);
|
||||
|
||||
/**
|
||||
* Traverse ourselves for cycle-collection
|
||||
@ -362,7 +365,7 @@ class ExternalResourceMap {
|
||||
|
||||
protected:
|
||||
class PendingLoad : public ExternalResourceLoad, public nsIStreamListener {
|
||||
~PendingLoad() {}
|
||||
~PendingLoad() = default;
|
||||
|
||||
public:
|
||||
explicit PendingLoad(Document* aDisplayDocument)
|
||||
@ -393,7 +396,7 @@ class ExternalResourceMap {
|
||||
friend class PendingLoad;
|
||||
|
||||
class LoadgroupCallbacks final : public nsIInterfaceRequestor {
|
||||
~LoadgroupCallbacks() {}
|
||||
~LoadgroupCallbacks() = default;
|
||||
|
||||
public:
|
||||
explicit LoadgroupCallbacks(nsIInterfaceRequestor* aOtherCallbacks)
|
||||
@ -464,6 +467,8 @@ class Document : public nsINode,
|
||||
public nsStubMutationObserver,
|
||||
public DispatcherTrait,
|
||||
public SupportsWeakPtr<Document> {
|
||||
friend class DocumentOrShadowRoot;
|
||||
|
||||
protected:
|
||||
explicit Document(const char* aContentType);
|
||||
virtual ~Document();
|
||||
@ -474,6 +479,12 @@ class Document : public nsINode,
|
||||
public:
|
||||
typedef dom::ExternalResourceMap::ExternalResourceLoad ExternalResourceLoad;
|
||||
typedef dom::ReferrerPolicy ReferrerPolicyEnum;
|
||||
using AdoptedStyleSheetCloneCache =
|
||||
nsRefPtrHashtable<nsPtrHashKey<const StyleSheet>, StyleSheet>;
|
||||
|
||||
// nsINode overrides the new operator for DOM Arena allocation.
|
||||
// to use the default one, we need to bring it back again
|
||||
void* operator new(size_t aSize) { return ::operator new(aSize); }
|
||||
|
||||
/**
|
||||
* Called when XPCOM shutdown.
|
||||
@ -565,6 +576,8 @@ class Document : public nsINode,
|
||||
return mIntrinsicStoragePrincipal;
|
||||
}
|
||||
|
||||
void ClearActiveStoragePrincipal() { mActiveStoragePrincipal = nullptr; }
|
||||
|
||||
nsIPrincipal* GetContentBlockingAllowListPrincipal() const {
|
||||
return mContentBlockingAllowListPrincipal;
|
||||
}
|
||||
@ -1103,6 +1116,14 @@ class Document : public nsINode,
|
||||
mHasUnsafeEvalCSP = aHasUnsafeEvalCSP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the document holds a CSP
|
||||
* delivered through an HTTP Header.
|
||||
*/
|
||||
bool GetHasCSPDeliveredThroughHeader() {
|
||||
return mHasCSPDeliveredThroughHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content blocking log.
|
||||
*/
|
||||
@ -1779,7 +1800,7 @@ class Document : public nsINode,
|
||||
|
||||
nsExpirationState* GetExpirationState() { return &mState; }
|
||||
|
||||
~SelectorCacheKey() { MOZ_COUNT_DTOR(SelectorCacheKey); }
|
||||
MOZ_COUNTED_DTOR(SelectorCacheKey)
|
||||
};
|
||||
|
||||
class SelectorCacheKeyDeleter;
|
||||
@ -1878,11 +1899,6 @@ class Document : public nsINode,
|
||||
InsertSheetAt(SheetCount(), *aSheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a stylesheet from the document
|
||||
*/
|
||||
void RemoveStyleSheet(StyleSheet&);
|
||||
|
||||
/**
|
||||
* Notify the document that the applicable state of the sheet changed
|
||||
* and that observers should be notified and style sets updated
|
||||
@ -1906,8 +1922,6 @@ class Document : public nsINode,
|
||||
return mAdditionalSheets[eAuthorSheet].SafeElementAt(0);
|
||||
}
|
||||
|
||||
void AppendAdoptedStyleSheet(StyleSheet& aSheet);
|
||||
|
||||
/**
|
||||
* Returns the index that aSheet should be inserted at to maintain document
|
||||
* ordering.
|
||||
@ -2028,14 +2042,14 @@ class Document : public nsINode,
|
||||
void RemoveFromNameTable(Element* aElement, nsAtom* aName);
|
||||
|
||||
/**
|
||||
* Returns all elements in the fullscreen stack in the insertion order.
|
||||
* Returns all elements in the top layer in the insertion order.
|
||||
*/
|
||||
nsTArray<Element*> GetFullscreenStack() const;
|
||||
nsTArray<Element*> GetTopLayer() const;
|
||||
|
||||
/**
|
||||
* Asynchronously requests that the document make aElement the fullscreen
|
||||
* element, and move into fullscreen mode. The current fullscreen element
|
||||
* (if any) is pushed onto the fullscreen element stack, and it can be
|
||||
* (if any) is pushed onto the top layer, and it can be
|
||||
* returned to fullscreen status by calling RestorePreviousFullscreenState().
|
||||
*
|
||||
* Note that requesting fullscreen in a document also makes the element which
|
||||
@ -2048,25 +2062,30 @@ class Document : public nsINode,
|
||||
// Do the "fullscreen element ready check" from the fullscreen spec.
|
||||
// It returns true if the given element is allowed to go into fullscreen.
|
||||
// It is responsive to dispatch "fullscreenerror" event when necessary.
|
||||
bool FullscreenElementReadyCheck(const FullscreenRequest&);
|
||||
bool FullscreenElementReadyCheck(FullscreenRequest&);
|
||||
|
||||
// This is called asynchronously by Document::AsyncRequestFullscreen()
|
||||
// to move this document into fullscreen mode if allowed.
|
||||
void RequestFullscreen(UniquePtr<FullscreenRequest> aRequest);
|
||||
|
||||
// Removes all elements from the fullscreen stack, removing full-scren
|
||||
// styles from the top element in the stack.
|
||||
// Removes all the elements with fullscreen flag set from the top layer, and
|
||||
// clears their fullscreen flag.
|
||||
void CleanupFullscreenState();
|
||||
|
||||
// Pushes aElement onto the fullscreen stack, and removes fullscreen styles
|
||||
// from the former fullscreen stack top, and its ancestors, and applies the
|
||||
// styles to aElement. aElement becomes the new "fullscreen element".
|
||||
bool FullscreenStackPush(Element* aElement);
|
||||
// Pushes aElement onto the top layer
|
||||
bool TopLayerPush(Element* aElement);
|
||||
|
||||
// Remove the top element from the fullscreen stack. Removes the fullscreen
|
||||
// styles from the former top element, and applies them to the new top
|
||||
// element, if there is one.
|
||||
void FullscreenStackPop();
|
||||
// Removes the topmost element which have aPredicate return true from the top
|
||||
// layer. The removed element, if any, is returned.
|
||||
Element* TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc);
|
||||
|
||||
// Pops the fullscreen element from the top layer and clears its
|
||||
// fullscreen flag.
|
||||
void UnsetFullscreenElement();
|
||||
|
||||
// Pushes the given element into the top of top layer and set fullscreen
|
||||
// flag.
|
||||
bool SetFullscreenElement(Element* aElement);
|
||||
|
||||
/**
|
||||
* Called when a frame in a child process has entered fullscreen or when a
|
||||
@ -2078,7 +2097,7 @@ class Document : public nsINode,
|
||||
|
||||
/**
|
||||
* Called when a frame in a remote child document has rolled back fullscreen
|
||||
* so that all its fullscreen element stacks are empty; we must continue the
|
||||
* so that all its top layer are empty; we must continue the
|
||||
* rollback in this parent process' doc tree branch which is fullscreen.
|
||||
* Note that only one branch of the document tree can have its documents in
|
||||
* fullscreen state at one time. We're in inconsistent state if a
|
||||
@ -2107,6 +2126,8 @@ class Document : public nsINode,
|
||||
*/
|
||||
Document* GetFullscreenRoot();
|
||||
|
||||
size_t CountFullscreenElements() const;
|
||||
|
||||
/**
|
||||
* Sets the fullscreen root to aRoot. This stores a weak reference to aRoot
|
||||
* in this document.
|
||||
@ -2412,8 +2433,8 @@ class Document : public nsINode,
|
||||
*/
|
||||
int32_t GetDefaultNamespaceID() const { return mDefaultElementType; }
|
||||
|
||||
void DeleteAllProperties();
|
||||
void DeleteAllPropertiesFor(nsINode* aNode);
|
||||
void RemoveAllProperties();
|
||||
void RemoveAllPropertiesFor(nsINode* aNode);
|
||||
|
||||
nsPropertyTable& PropertyTable() { return mPropertyTable; }
|
||||
|
||||
@ -2438,8 +2459,8 @@ class Document : public nsINode,
|
||||
* The enumerator callback should return true to continue enumerating, or
|
||||
* false to stop. This will never get passed a null aDocument.
|
||||
*/
|
||||
typedef bool (*SubDocEnumFunc)(Document&, void* aData);
|
||||
void EnumerateSubDocuments(SubDocEnumFunc aCallback, void* aData);
|
||||
using SubDocEnumFunc = FunctionRef<bool(Document&)>;
|
||||
void EnumerateSubDocuments(SubDocEnumFunc aCallback);
|
||||
|
||||
/**
|
||||
* Collect all the descendant documents for which |aCalback| returns true.
|
||||
@ -2768,7 +2789,7 @@ class Document : public nsINode,
|
||||
* The enumerator callback should return true to continue enumerating, or
|
||||
* false to stop. This callback will never get passed a null aDocument.
|
||||
*/
|
||||
void EnumerateExternalResources(SubDocEnumFunc aCallback, void* aData);
|
||||
void EnumerateExternalResources(SubDocEnumFunc aCallback);
|
||||
|
||||
dom::ExternalResourceMap& ExternalResourceMap() {
|
||||
return mExternalResourceMap;
|
||||
@ -2836,9 +2857,8 @@ class Document : public nsINode,
|
||||
void RegisterActivityObserver(nsISupports* aSupports);
|
||||
bool UnregisterActivityObserver(nsISupports* aSupports);
|
||||
// Enumerate all the observers in mActivityObservers by the aEnumerator.
|
||||
typedef void (*ActivityObserverEnumerator)(nsISupports*, void*);
|
||||
void EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
|
||||
void* aData);
|
||||
using ActivityObserverEnumerator = FunctionRef<void(nsISupports*)>;
|
||||
void EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator);
|
||||
|
||||
// Indicates whether mAnimationController has been (lazily) initialized.
|
||||
// If this returns true, we're promising that GetAnimationController()
|
||||
@ -2986,7 +3006,7 @@ class Document : public nsINode,
|
||||
* Note that static documents are also "loaded as data" (if this method
|
||||
* returns true, IsLoadedAsData() will also return true).
|
||||
*/
|
||||
bool IsStaticDocument() { return mIsStaticDocument; }
|
||||
bool IsStaticDocument() const { return mIsStaticDocument; }
|
||||
|
||||
/**
|
||||
* Clones the document along with any subdocuments, stylesheet, etc.
|
||||
@ -3490,6 +3510,7 @@ class Document : public nsINode,
|
||||
mozilla::ErrorResult& rv);
|
||||
Nullable<WindowProxyHolder> GetDefaultView() const;
|
||||
Element* GetActiveElement();
|
||||
nsIContent* GetUnretargetedFocusedContent() const;
|
||||
bool HasFocus(ErrorResult& rv) const;
|
||||
void GetDesignMode(nsAString& aDesignMode);
|
||||
void SetDesignMode(const nsAString& aDesignMode,
|
||||
@ -3526,7 +3547,9 @@ class Document : public nsINode,
|
||||
nsIURI* GetDocumentURIObject() const;
|
||||
// Not const because all the fullscreen goop is not const
|
||||
bool FullscreenEnabled(CallerType aCallerType);
|
||||
Element* FullscreenStackTop();
|
||||
Element* GetTopLayerTop();
|
||||
// Return the fullscreen element in the top layer
|
||||
Element* GetUnretargetedFullScreenElement();
|
||||
bool Fullscreen() { return !!GetFullscreenElement(); }
|
||||
already_AddRefed<Promise> ExitFullscreen(ErrorResult&);
|
||||
void ExitPointerLock() { UnlockPointer(this); }
|
||||
@ -3718,16 +3741,6 @@ class Document : public nsINode,
|
||||
|
||||
bool IsSynthesized();
|
||||
|
||||
void AddToVisibleContentHeuristic(size_t aNumber) {
|
||||
if (MOZ_UNLIKELY(SIZE_MAX - mVisibleContentHeuristic < aNumber)) {
|
||||
mVisibleContentHeuristic = SIZE_MAX;
|
||||
} else {
|
||||
mVisibleContentHeuristic += aNumber;
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetVisibleContentHeuristic() const { return mVisibleContentHeuristic; }
|
||||
|
||||
// Called to track whether this document has had any interaction.
|
||||
// This is used to track whether we should permit "beforeunload".
|
||||
void SetUserHasInteracted();
|
||||
@ -3777,6 +3790,8 @@ class Document : public nsINode,
|
||||
return mDocGroup;
|
||||
}
|
||||
|
||||
DocGroup* GetDocGroupOrCreate();
|
||||
|
||||
/**
|
||||
* If we're a sub-document, the parent document's layout can affect our style
|
||||
* and layout (due to the viewport size, viewport units, media queries...).
|
||||
@ -4052,6 +4067,9 @@ class Document : public nsINode,
|
||||
bool InRDMPane() const { return mInRDMPane; }
|
||||
void SetInRDMPane(bool aInRDMPane) { mInRDMPane = aInRDMPane; }
|
||||
|
||||
// CSS prefers-color-scheme media feature for this document.
|
||||
StylePrefersColorScheme PrefersColorScheme() const;
|
||||
|
||||
// Returns true if we use overlay scrollbars on the system wide or on the
|
||||
// given document.
|
||||
static bool UseOverlayScrollbars(const Document* aDocument);
|
||||
@ -4060,10 +4078,6 @@ class Document : public nsINode,
|
||||
|
||||
static bool AutomaticStorageAccessCanBeGranted(nsIPrincipal* aPrincipal);
|
||||
|
||||
void SetAdoptedStyleSheets(
|
||||
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
|
||||
ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
void DoUpdateSVGUseElementShadowTrees();
|
||||
|
||||
@ -4104,8 +4118,6 @@ class Document : public nsINode,
|
||||
bool ApplyFullscreen(UniquePtr<FullscreenRequest>);
|
||||
|
||||
void RemoveDocStyleSheetsFromStyleSets();
|
||||
void RemoveStyleSheetsFromStyleSets(
|
||||
const nsTArray<RefPtr<StyleSheet>>& aSheets, StyleOrigin);
|
||||
void ResetStylesheetsToURI(nsIURI* aURI);
|
||||
void FillStyleSet();
|
||||
void FillStyleSetUserAndUASheets();
|
||||
@ -4118,8 +4130,8 @@ class Document : public nsINode,
|
||||
}
|
||||
void AddContentEditableStyleSheetsToStyleSet(bool aDesignMode);
|
||||
void RemoveContentEditableStyleSheets();
|
||||
void AddStyleSheetToStyleSets(StyleSheet* aSheet);
|
||||
void RemoveStyleSheetFromStyleSets(StyleSheet* aSheet);
|
||||
void AddStyleSheetToStyleSets(StyleSheet&);
|
||||
void RemoveStyleSheetFromStyleSets(StyleSheet&);
|
||||
void NotifyStyleSheetApplicableStateChanged();
|
||||
// Just like EnableStyleSheetsForSet, but doesn't check whether
|
||||
// aSheetSet is null and allows the caller to control whether to set
|
||||
@ -4564,6 +4576,9 @@ class Document : public nsINode,
|
||||
// True if a document load has a CSP with unsafe-inline attached.
|
||||
bool mHasUnsafeInlineCSP : 1;
|
||||
|
||||
// True if the document has a CSP delivered throuh a header
|
||||
bool mHasCSPDeliveredThroughHeader : 1;
|
||||
|
||||
// True if DisallowBFCaching has been called on this document.
|
||||
bool mBFCacheDisallowed : 1;
|
||||
|
||||
@ -4917,16 +4932,6 @@ class Document : public nsINode,
|
||||
// Array of observers
|
||||
nsTObserverArray<nsIDocumentObserver*> mObservers;
|
||||
|
||||
// An ever-increasing heuristic number that is higher the more content is
|
||||
// likely to be visible in the page.
|
||||
//
|
||||
// Right now it effectively measures amount of text content that has ever been
|
||||
// connected to the document in some way, and is not under a <script> or
|
||||
// <style>.
|
||||
//
|
||||
// Note that this is only measured during load.
|
||||
size_t mVisibleContentHeuristic = 0;
|
||||
|
||||
// Whether the user has interacted with the document or not:
|
||||
bool mUserHasInteracted;
|
||||
|
||||
@ -5019,10 +5024,8 @@ class Document : public nsINode,
|
||||
// Array of intersection observers
|
||||
nsTHashtable<nsPtrHashKey<DOMIntersectionObserver>> mIntersectionObservers;
|
||||
|
||||
// Stack of fullscreen elements. When we request fullscreen we push the
|
||||
// fullscreen element onto this stack, and when we cancel fullscreen we
|
||||
// pop one off this stack, restoring the previous fullscreen state
|
||||
nsTArray<nsWeakPtr> mFullscreenStack;
|
||||
// Stack of top layer elements.
|
||||
nsTArray<nsWeakPtr> mTopLayer;
|
||||
|
||||
// The root of the doc tree in which this document is in. This is only
|
||||
// non-null when this document is in fullscreen mode.
|
||||
@ -5170,6 +5173,11 @@ class Document : public nsINode,
|
||||
// The principal to use for the storage area of this document.
|
||||
nsCOMPtr<nsIPrincipal> mIntrinsicStoragePrincipal;
|
||||
|
||||
// The cached storage principal for this document.
|
||||
// This is mutable so that we can keep EffectiveStoragePrincipal() const
|
||||
// which is required due to its CloneDocHelper() call site. :-(
|
||||
mutable nsCOMPtr<nsIPrincipal> mActiveStoragePrincipal;
|
||||
|
||||
// The principal to use for the content blocking allow list.
|
||||
nsCOMPtr<nsIPrincipal> mContentBlockingAllowListPrincipal;
|
||||
|
||||
|
@ -82,7 +82,7 @@ class DocumentFragment : public FragmentOrElement {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~DocumentFragment() {}
|
||||
virtual ~DocumentFragment() = default;
|
||||
|
||||
nsresult Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
|
||||
RefPtr<Element> mHost;
|
||||
|
@ -71,35 +71,44 @@ StyleSheetList* DocumentOrShadowRoot::StyleSheets() {
|
||||
}
|
||||
|
||||
void DocumentOrShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
|
||||
aSheet.SetAssociatedDocumentOrShadowRoot(
|
||||
this, StyleSheet::OwnedByDocumentOrShadowRoot);
|
||||
aSheet.SetAssociatedDocumentOrShadowRoot(this);
|
||||
mStyleSheets.InsertElementAt(aIndex, &aSheet);
|
||||
}
|
||||
|
||||
void DocumentOrShadowRoot::InsertAdoptedSheetAt(size_t aIndex,
|
||||
StyleSheet& aSheet) {
|
||||
mAdoptedStyleSheets.InsertElementAt(aIndex, &aSheet);
|
||||
}
|
||||
|
||||
already_AddRefed<StyleSheet> DocumentOrShadowRoot::RemoveSheet(
|
||||
StyleSheet& aSheet) {
|
||||
void DocumentOrShadowRoot::RemoveStyleSheet(StyleSheet& aSheet) {
|
||||
auto index = mStyleSheets.IndexOf(&aSheet);
|
||||
if (index == mStyleSheets.NoIndex) {
|
||||
return nullptr;
|
||||
// We should only hit this case if we are unlinking
|
||||
// in which case mStyleSheets should be cleared.
|
||||
MOZ_ASSERT(mKind != Kind::Document ||
|
||||
AsNode().AsDocument()->InUnlinkOrDeletion());
|
||||
MOZ_ASSERT(mStyleSheets.IsEmpty());
|
||||
return;
|
||||
}
|
||||
RefPtr<StyleSheet> sheet = std::move(mStyleSheets[index]);
|
||||
mStyleSheets.RemoveElementAt(index);
|
||||
RemoveSheetFromStylesIfApplicable(*sheet);
|
||||
sheet->ClearAssociatedDocumentOrShadowRoot();
|
||||
return sheet.forget();
|
||||
}
|
||||
|
||||
void DocumentOrShadowRoot::RemoveSheetFromStylesIfApplicable(
|
||||
StyleSheet& aSheet) {
|
||||
if (!aSheet.IsApplicable()) {
|
||||
return;
|
||||
}
|
||||
if (mKind == Kind::Document) {
|
||||
AsNode().AsDocument()->RemoveStyleSheetFromStyleSets(aSheet);
|
||||
} else {
|
||||
MOZ_ASSERT(AsNode().IsShadowRoot());
|
||||
static_cast<ShadowRoot&>(AsNode()).RemoveSheetFromStyles(aSheet);
|
||||
}
|
||||
}
|
||||
|
||||
// https://wicg.github.io/construct-stylesheets/#dom-documentorshadowroot-adoptedstylesheets
|
||||
void DocumentOrShadowRoot::EnsureAdoptedSheetsAreValid(
|
||||
void DocumentOrShadowRoot::SetAdoptedStyleSheets(
|
||||
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
|
||||
ErrorResult& aRv) {
|
||||
nsTHashtable<nsPtrHashKey<const StyleSheet>> set(
|
||||
aAdoptedStyleSheets.Length());
|
||||
|
||||
Document& doc = *AsNode().OwnerDoc();
|
||||
for (const OwningNonNull<StyleSheet>& sheet : aAdoptedStyleSheets) {
|
||||
// 2.1 Check if all sheets are constructed, else throw NotAllowedError
|
||||
if (!sheet->IsConstructed()) {
|
||||
@ -109,23 +118,117 @@ void DocumentOrShadowRoot::EnsureAdoptedSheetsAreValid(
|
||||
}
|
||||
// 2.2 Check if all sheets' constructor documents match the
|
||||
// DocumentOrShadowRoot's node document, else throw NotAlloweError
|
||||
if (!sheet->ConstructorDocumentMatches(AsNode().OwnerDoc())) {
|
||||
if (!sheet->ConstructorDocumentMatches(doc)) {
|
||||
return aRv.ThrowNotAllowedError(
|
||||
"Each adopted style sheet's constructor document must match the "
|
||||
"document or shadow root's node document");
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(nordzilla): This is temporary code to disallow duplicate sheets.
|
||||
// This exists to ensure that the fuzzers aren't blocked.
|
||||
// This code will eventually be removed.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1617302
|
||||
if (!set.EnsureInserted(sheet.get())) {
|
||||
return aRv.ThrowNotAllowedError(
|
||||
"Temporarily disallowing duplicate stylesheets.");
|
||||
auto* shadow = ShadowRoot::FromNode(AsNode());
|
||||
MOZ_ASSERT((mKind == Kind::ShadowRoot) == !!shadow);
|
||||
|
||||
StyleSheetSet set(aAdoptedStyleSheets.Length());
|
||||
size_t commonPrefix = 0;
|
||||
|
||||
// Find the index at which the new array differs from the old array.
|
||||
// We don't want to do extra work for the sheets that both arrays have.
|
||||
size_t min =
|
||||
std::min(aAdoptedStyleSheets.Length(), mAdoptedStyleSheets.Length());
|
||||
for (size_t i = 0; i < min; ++i) {
|
||||
if (aAdoptedStyleSheets[i] != mAdoptedStyleSheets[i]) {
|
||||
break;
|
||||
}
|
||||
++commonPrefix;
|
||||
set.PutEntry(mAdoptedStyleSheets[i]);
|
||||
}
|
||||
|
||||
// Try to truncate the sheets to a common prefix.
|
||||
// If the prefix contains duplicates of sheets that we are removing,
|
||||
// we are just going to re-build everything from scratch.
|
||||
if (commonPrefix != mAdoptedStyleSheets.Length()) {
|
||||
StyleSheetSet removedSet(mAdoptedStyleSheets.Length() - commonPrefix);
|
||||
for (size_t i = mAdoptedStyleSheets.Length(); i != commonPrefix; --i) {
|
||||
StyleSheet* sheetToRemove = mAdoptedStyleSheets.ElementAt(i - 1);
|
||||
if (MOZ_UNLIKELY(set.Contains(sheetToRemove))) {
|
||||
// Fixing duplicate sheets would require insertions/removals from the
|
||||
// style set. We may as well just rebuild the whole thing from scratch.
|
||||
set.Clear();
|
||||
// Note that setting this to zero means we'll continue the loop until
|
||||
// all the sheets are cleared.
|
||||
commonPrefix = 0;
|
||||
}
|
||||
if (MOZ_LIKELY(removedSet.EnsureInserted(sheetToRemove))) {
|
||||
RemoveSheetFromStylesIfApplicable(*sheetToRemove);
|
||||
sheetToRemove->RemoveAdopter(*this);
|
||||
}
|
||||
}
|
||||
mAdoptedStyleSheets.TruncateLength(commonPrefix);
|
||||
}
|
||||
|
||||
// 3. Set the adopted style sheets to the new sheets
|
||||
mAdoptedStyleSheets.SetCapacity(aAdoptedStyleSheets.Length());
|
||||
|
||||
// Only add sheets that are not already in the common prefix.
|
||||
for (const auto& sheet : MakeSpan(aAdoptedStyleSheets).From(commonPrefix)) {
|
||||
if (MOZ_UNLIKELY(!set.EnsureInserted(sheet))) {
|
||||
// The idea is that this case is rare, so we pay the price of removing the
|
||||
// old sheet from the styles and append it later rather than the other way
|
||||
// around.
|
||||
RemoveSheetFromStylesIfApplicable(*sheet);
|
||||
} else {
|
||||
sheet->AddAdopter(*this);
|
||||
}
|
||||
mAdoptedStyleSheets.AppendElement(sheet);
|
||||
if (sheet->IsApplicable()) {
|
||||
if (mKind == Kind::Document) {
|
||||
doc.AddStyleSheetToStyleSets(*sheet);
|
||||
} else {
|
||||
shadow->InsertSheetIntoAuthorData(mAdoptedStyleSheets.Length() - 1,
|
||||
*sheet, mAdoptedStyleSheets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentOrShadowRoot::ClearAdoptedStyleSheets() {
|
||||
EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
|
||||
RemoveSheetFromStylesIfApplicable(aSheet);
|
||||
aSheet.RemoveAdopter(*this);
|
||||
});
|
||||
mAdoptedStyleSheets.Clear();
|
||||
}
|
||||
|
||||
void DocumentOrShadowRoot::CloneAdoptedSheetsFrom(
|
||||
const DocumentOrShadowRoot& aSource) {
|
||||
if (!aSource.AdoptedSheetCount()) {
|
||||
return;
|
||||
}
|
||||
Sequence<OwningNonNull<StyleSheet>> list;
|
||||
if (!list.SetCapacity(mAdoptedStyleSheets.Length(), fallible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Document& ownerDoc = *AsNode().OwnerDoc();
|
||||
const Document& sourceDoc = *aSource.AsNode().OwnerDoc();
|
||||
auto* clonedSheetMap = static_cast<Document::AdoptedStyleSheetCloneCache*>(
|
||||
sourceDoc.GetProperty(nsGkAtoms::adoptedsheetclones));
|
||||
MOZ_ASSERT(clonedSheetMap);
|
||||
|
||||
for (const StyleSheet* sheet : aSource.mAdoptedStyleSheets) {
|
||||
RefPtr<StyleSheet> clone = clonedSheetMap->LookupForAdd(sheet).OrInsert(
|
||||
[&] { return sheet->CloneAdoptedSheet(ownerDoc); });
|
||||
MOZ_ASSERT(clone);
|
||||
MOZ_DIAGNOSTIC_ASSERT(clone->ConstructorDocumentMatches(ownerDoc));
|
||||
DebugOnly<bool> succeeded = list.AppendElement(std::move(clone), fallible);
|
||||
MOZ_ASSERT(succeeded);
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
SetAdoptedStyleSheets(list, rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
}
|
||||
|
||||
Element* DocumentOrShadowRoot::GetElementById(const nsAString& aElementId) {
|
||||
if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
|
||||
nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
|
||||
@ -184,25 +287,13 @@ nsIContent* DocumentOrShadowRoot::Retarget(nsIContent* aContent) const {
|
||||
}
|
||||
|
||||
Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
|
||||
if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) {
|
||||
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
|
||||
nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
|
||||
window, nsFocusManager::eOnlyCurrentWindow,
|
||||
getter_AddRefs(focusedWindow));
|
||||
// be safe and make sure the element is from this document
|
||||
if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
|
||||
if (focusedContent->ChromeOnlyAccess()) {
|
||||
focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
|
||||
}
|
||||
|
||||
if (focusedContent) {
|
||||
if (nsIContent* retarget = Retarget(focusedContent)) {
|
||||
return retarget->AsElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
|
||||
if (!content) {
|
||||
return nullptr;
|
||||
}
|
||||
if (nsIContent* retarget = Retarget(content)) {
|
||||
return retarget->AsElement();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -225,7 +316,7 @@ Element* DocumentOrShadowRoot::GetFullscreenElement() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element* element = AsNode().OwnerDoc()->FullscreenStackTop();
|
||||
Element* element = AsNode().OwnerDoc()->GetUnretargetedFullScreenElement();
|
||||
NS_ASSERTION(!element || element->State().HasState(NS_EVENT_STATE_FULLSCREEN),
|
||||
"Fullscreen element should have fullscreen styles applied");
|
||||
|
||||
@ -510,7 +601,7 @@ void DocumentOrShadowRoot::GetAnimations(
|
||||
child = child->GetNextSibling()) {
|
||||
if (RefPtr<Element> element = Element::FromNode(child)) {
|
||||
nsTArray<RefPtr<Animation>> result;
|
||||
element->GetAnimations(options, result, Element::Flush::No);
|
||||
element->GetAnimationsWithoutFlush(options, result);
|
||||
aAnimations.AppendElements(std::move(result));
|
||||
}
|
||||
}
|
||||
@ -660,7 +751,9 @@ nsRadioGroupStruct* DocumentOrShadowRoot::GetOrCreateRadioGroup(
|
||||
int32_t DocumentOrShadowRoot::StyleOrderIndexOfSheet(
|
||||
const StyleSheet& aSheet) const {
|
||||
if (aSheet.IsConstructed()) {
|
||||
int32_t index = mAdoptedStyleSheets.IndexOf(&aSheet);
|
||||
// NOTE: constructable sheets can have duplicates, so we need to start
|
||||
// looking from behind.
|
||||
int32_t index = mAdoptedStyleSheets.LastIndexOf(&aSheet);
|
||||
return (index < 0) ? index : index + SheetCount();
|
||||
}
|
||||
return mStyleSheets.IndexOf(&aSheet);
|
||||
@ -677,26 +770,27 @@ void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdoptedStyleSheets)
|
||||
|
||||
auto NoteSheets = [tmp, &cb = cb](nsTArray<RefPtr<StyleSheet>>& sheetList) {
|
||||
for (StyleSheet* sheet : sheetList) {
|
||||
if (!sheet->IsApplicable()) {
|
||||
continue;
|
||||
}
|
||||
// The style set or mServoStyles keep more references to it if the sheet
|
||||
// is applicable.
|
||||
if (tmp->mKind == Kind::ShadowRoot) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
|
||||
cb.NoteXPCOMChild(sheet);
|
||||
} else if (tmp->AsNode().AsDocument()->StyleSetFilled()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mStyleSet->mRawSet.stylist.stylesheets.author[i]");
|
||||
cb.NoteXPCOMChild(sheet);
|
||||
}
|
||||
auto NoteSheetIfApplicable = [&](StyleSheet& aSheet) {
|
||||
if (!aSheet.IsApplicable()) {
|
||||
return;
|
||||
}
|
||||
// The style set or mServoStyles keep more references to it if the sheet
|
||||
// is applicable.
|
||||
if (tmp->mKind == Kind::ShadowRoot) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
|
||||
cb.NoteXPCOMChild(&aSheet);
|
||||
} else if (tmp->AsNode().AsDocument()->StyleSetFilled()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mStyleSet->mRawSet.stylist.stylesheets.author[i]");
|
||||
cb.NoteXPCOMChild(&aSheet);
|
||||
}
|
||||
};
|
||||
|
||||
NoteSheets(tmp->mStyleSheets);
|
||||
NoteSheets(tmp->mAdoptedStyleSheets);
|
||||
for (auto& sheet : tmp->mStyleSheets) {
|
||||
NoteSheetIfApplicable(*sheet);
|
||||
}
|
||||
|
||||
tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront(NoteSheetIfApplicable);
|
||||
|
||||
for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
iter.Get()->Traverse(&cb);
|
||||
@ -719,7 +813,12 @@ void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
|
||||
|
||||
void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets);
|
||||
for (RefPtr<StyleSheet>& sheet : tmp->mAdoptedStyleSheets) {
|
||||
for (StyleSheet* sheet : tmp->mStyleSheets) {
|
||||
sheet->ClearAssociatedDocumentOrShadowRoot();
|
||||
tmp->RemoveSheetFromStylesIfApplicable(*sheet);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheets);
|
||||
for (StyleSheet* sheet : tmp->mAdoptedStyleSheets) {
|
||||
sheet->RemoveAdopter(*tmp);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);
|
||||
|
@ -86,6 +86,8 @@ class DocumentOrShadowRoot {
|
||||
|
||||
void GetAdoptedStyleSheets(nsTArray<RefPtr<StyleSheet>>&) const;
|
||||
|
||||
void RemoveStyleSheet(StyleSheet&);
|
||||
|
||||
Element* GetElementById(const nsAString& aElementId);
|
||||
|
||||
/**
|
||||
@ -219,16 +221,38 @@ class DocumentOrShadowRoot {
|
||||
|
||||
nsIContent* Retarget(nsIContent* aContent) const;
|
||||
|
||||
protected:
|
||||
// Returns the reference to the sheet, if found in mStyleSheets.
|
||||
already_AddRefed<StyleSheet> RemoveSheet(StyleSheet& aSheet);
|
||||
void InsertSheetAt(size_t aIndex, StyleSheet& aSheet);
|
||||
void InsertAdoptedSheetAt(size_t aIndex, StyleSheet& aSheet);
|
||||
|
||||
void EnsureAdoptedSheetsAreValid(
|
||||
void SetAdoptedStyleSheets(
|
||||
const Sequence<OwningNonNull<StyleSheet>>& aAdoptedStyleSheets,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// This is needed because ServoStyleSet / ServoAuthorData don't deal with
|
||||
// duplicate stylesheets (and it's unclear we'd want to support that as it'd
|
||||
// be a bunch of duplicate work), while adopted stylesheets do need to deal
|
||||
// with them.
|
||||
template <typename Callback>
|
||||
void EnumerateUniqueAdoptedStyleSheetsBackToFront(Callback aCallback) {
|
||||
StyleSheetSet set(mAdoptedStyleSheets.Length());
|
||||
for (StyleSheet* sheet : Reversed(mAdoptedStyleSheets)) {
|
||||
if (MOZ_UNLIKELY(!set.EnsureInserted(sheet))) {
|
||||
continue;
|
||||
}
|
||||
aCallback(*sheet);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
using StyleSheetSet = nsTHashtable<nsPtrHashKey<const StyleSheet>>;
|
||||
void RemoveSheetFromStylesIfApplicable(StyleSheet&);
|
||||
void ClearAdoptedStyleSheets();
|
||||
|
||||
/**
|
||||
* Clone's the argument's adopted style sheets into this.
|
||||
* This should only be used when cloning a static document for printing.
|
||||
*/
|
||||
void CloneAdoptedSheetsFrom(const DocumentOrShadowRoot&);
|
||||
|
||||
void InsertSheetAt(size_t aIndex, StyleSheet& aSheet);
|
||||
|
||||
void AddSizeOfExcludingThis(nsWindowSizes&) const;
|
||||
void AddSizeOfOwnedSheetArrayExcludingThis(
|
||||
nsWindowSizes&, const nsTArray<RefPtr<StyleSheet>>&) const;
|
||||
@ -243,8 +267,10 @@ class DocumentOrShadowRoot {
|
||||
nsTArray<RefPtr<StyleSheet>> mStyleSheets;
|
||||
RefPtr<StyleSheetList> mDOMStyleSheets;
|
||||
|
||||
// Style sheets that are adopted by assinging to the `adoptedStyleSheets`
|
||||
// WebIDL atribute. These can only be constructed stylesheets.
|
||||
/**
|
||||
* Style sheets that are adopted by assinging to the `adoptedStyleSheets`
|
||||
* WebIDL atribute. These can only be constructed stylesheets.
|
||||
*/
|
||||
nsTArray<RefPtr<StyleSheet>> mAdoptedStyleSheets;
|
||||
|
||||
/*
|
||||
|
@ -25,8 +25,9 @@ already_AddRefed<mozilla::dom::DocumentType> NS_NewDOMDocumentType(
|
||||
nsGkAtoms::documentTypeNodeName, nullptr, kNameSpaceID_None,
|
||||
nsINode::DOCUMENT_TYPE_NODE, aName);
|
||||
|
||||
RefPtr<mozilla::dom::DocumentType> docType = new mozilla::dom::DocumentType(
|
||||
ni.forget(), aPublicId, aSystemId, aInternalSubset);
|
||||
RefPtr<mozilla::dom::DocumentType> docType =
|
||||
new (aNodeInfoManager) mozilla::dom::DocumentType(
|
||||
ni.forget(), aPublicId, aSystemId, aInternalSubset);
|
||||
return docType.forget();
|
||||
}
|
||||
|
||||
@ -73,8 +74,8 @@ void DocumentType::GetInternalSubset(nsAString& aInternalSubset) const {
|
||||
|
||||
already_AddRefed<CharacterData> DocumentType::CloneDataNode(
|
||||
mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const {
|
||||
return do_AddRef(new DocumentType(do_AddRef(aNodeInfo), mPublicId, mSystemId,
|
||||
mInternalSubset));
|
||||
return do_AddRef(new (aNodeInfo->NodeInfoManager()) DocumentType(
|
||||
do_AddRef(aNodeInfo), mPublicId, mSystemId, mInternalSubset));
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/dom/ScriptLoader.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "nsAtom.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
@ -260,8 +261,7 @@ Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) {
|
||||
}
|
||||
|
||||
EventStates Element::IntrinsicState() const {
|
||||
return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE
|
||||
: NS_EVENT_STATE_MOZ_READONLY;
|
||||
return IsEditable() ? NS_EVENT_STATE_READWRITE : NS_EVENT_STATE_READONLY;
|
||||
}
|
||||
|
||||
void Element::NotifyStateChange(EventStates aStates) {
|
||||
@ -299,13 +299,26 @@ void Element::UpdateState(bool aNotify) {
|
||||
} // namespace mozilla
|
||||
|
||||
void nsIContent::UpdateEditableState(bool aNotify) {
|
||||
nsIContent* parent = GetParent();
|
||||
if (IsInNativeAnonymousSubtree()) {
|
||||
// Don't propagate the editable flag into native anonymous subtrees.
|
||||
if (IsRootOfNativeAnonymousSubtree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't implicitly set the flag on the root of a native anonymous subtree.
|
||||
// This needs to be set explicitly, see for example
|
||||
// nsTextControlFrame::CreateRootNode().
|
||||
SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE) &&
|
||||
!IsRootOfNativeAnonymousSubtree());
|
||||
// We allow setting the flag on NAC (explicitly, see
|
||||
// nsTextControlFrame::CreateAnonymousContent for example), but not
|
||||
// unsetting it.
|
||||
//
|
||||
// Otherwise, just the act of binding the NAC subtree into our non-anonymous
|
||||
// parent would clear the flag, which is not good. As we shouldn't move NAC
|
||||
// around, this is fine.
|
||||
if (HasFlag(NODE_IS_EDITABLE)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent* parent = GetParent();
|
||||
SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
@ -321,19 +334,28 @@ void Element::UpdateEditableState(bool aNotify) {
|
||||
// insertion into the document and UpdateState can be slow for
|
||||
// some kinds of elements even when not notifying.
|
||||
if (IsEditable()) {
|
||||
RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
|
||||
AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
|
||||
RemoveStatesSilently(NS_EVENT_STATE_READONLY);
|
||||
AddStatesSilently(NS_EVENT_STATE_READWRITE);
|
||||
} else {
|
||||
RemoveStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
|
||||
AddStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
|
||||
RemoveStatesSilently(NS_EVENT_STATE_READWRITE);
|
||||
AddStatesSilently(NS_EVENT_STATE_READONLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Element::TabIndex() {
|
||||
const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::tabindex);
|
||||
Maybe<int32_t> Element::GetTabIndexAttrValue() {
|
||||
const nsAttrValue* attrVal = GetParsedAttr(nsGkAtoms::tabindex);
|
||||
if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
|
||||
return attrVal->GetIntegerValue();
|
||||
return Some(attrVal->GetIntegerValue());
|
||||
}
|
||||
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
int32_t Element::TabIndex() {
|
||||
Maybe<int32_t> attrVal = GetTabIndexAttrValue();
|
||||
if (attrVal.isSome()) {
|
||||
return attrVal.value();
|
||||
}
|
||||
|
||||
return TabIndexDefault();
|
||||
@ -341,21 +363,21 @@ int32_t Element::TabIndex() {
|
||||
|
||||
void Element::Focus(const FocusOptions& aOptions, ErrorResult& aError) {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (!fm) {
|
||||
return;
|
||||
}
|
||||
// Also other browsers seem to have the hack to not re-focus (and flush) when
|
||||
// the element is already focused.
|
||||
// Until https://github.com/whatwg/html/issues/4512 is clarified, we'll
|
||||
// maintain interoperatibility by not re-focusing, independent of aOptions.
|
||||
// I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll:
|
||||
// false })` won't re-focus.
|
||||
if (fm) {
|
||||
if (fm->CanSkipFocus(this)) {
|
||||
fm->NeedsFlushBeforeEventHandling(this);
|
||||
} else {
|
||||
aError = fm->SetFocus(
|
||||
this, nsIFocusManager::FLAG_BYELEMENTFOCUS |
|
||||
nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions));
|
||||
}
|
||||
if (fm->CanSkipFocus(this)) {
|
||||
fm->NeedsFlushBeforeEventHandling(this);
|
||||
return;
|
||||
}
|
||||
aError = fm->SetFocus(
|
||||
this, nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions));
|
||||
}
|
||||
|
||||
void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) {
|
||||
@ -493,7 +515,7 @@ void Element::UnlockStyleStates(EventStates aStates) {
|
||||
locks->mLocks &= ~aStates;
|
||||
|
||||
if (locks->mLocks.IsEmpty()) {
|
||||
DeleteProperty(nsGkAtoms::lockedStyleStates);
|
||||
RemoveProperty(nsGkAtoms::lockedStyleStates);
|
||||
ClearHasLockedStyleStates();
|
||||
delete locks;
|
||||
} else {
|
||||
@ -507,7 +529,7 @@ void Element::UnlockStyleStates(EventStates aStates) {
|
||||
void Element::ClearStyleStateLocks() {
|
||||
StyleStateLocks locks = LockedStyleStates();
|
||||
|
||||
DeleteProperty(nsGkAtoms::lockedStyleStates);
|
||||
RemoveProperty(nsGkAtoms::lockedStyleStates);
|
||||
ClearHasLockedStyleStates();
|
||||
|
||||
NotifyStyleStateChange(locks.mLocks);
|
||||
@ -967,7 +989,7 @@ nsRect Element::GetClientAreaRect() {
|
||||
// document, we have overlay scrollbars, and we aren't embedded in another
|
||||
// document
|
||||
bool overlayScrollbars =
|
||||
LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0;
|
||||
LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0;
|
||||
bool rootContentDocument =
|
||||
presContext && presContext->IsRootContentDocument();
|
||||
if (overlayScrollbars && rootContentDocument &&
|
||||
@ -1218,8 +1240,9 @@ already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
|
||||
* context object's node document, host is context object,
|
||||
* and mode is init's mode.
|
||||
*/
|
||||
auto* nim = nodeInfo->NodeInfoManager();
|
||||
RefPtr<ShadowRoot> shadowRoot =
|
||||
new ShadowRoot(this, aMode, nodeInfo.forget());
|
||||
new (nim) ShadowRoot(this, aMode, nodeInfo.forget());
|
||||
|
||||
if (NodeOrAncestorHasDirAuto()) {
|
||||
shadowRoot->SetAncestorHasDirAuto();
|
||||
@ -1726,7 +1749,6 @@ nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||
// Now recurse into our kids. Ensure this happens after binding the shadow
|
||||
// root so that directionality of slots is updated.
|
||||
{
|
||||
BindContext::NestingLevel level(aContext, *this);
|
||||
for (nsIContent* child = GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
rv = child->BindToTree(aContext, *this);
|
||||
@ -1911,14 +1933,14 @@ void Element::UnbindFromTree(bool aNullParent) {
|
||||
//
|
||||
// FIXME (Bug 522599): Need a test for this.
|
||||
if (MayHaveAnimations()) {
|
||||
DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty);
|
||||
DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
|
||||
DeleteProperty(nsGkAtoms::transitionsOfMarkerProperty);
|
||||
DeleteProperty(nsGkAtoms::transitionsProperty);
|
||||
DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
|
||||
DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
|
||||
DeleteProperty(nsGkAtoms::animationsOfMarkerProperty);
|
||||
DeleteProperty(nsGkAtoms::animationsProperty);
|
||||
RemoveProperty(nsGkAtoms::transitionsOfBeforeProperty);
|
||||
RemoveProperty(nsGkAtoms::transitionsOfAfterProperty);
|
||||
RemoveProperty(nsGkAtoms::transitionsOfMarkerProperty);
|
||||
RemoveProperty(nsGkAtoms::transitionsProperty);
|
||||
RemoveProperty(nsGkAtoms::animationsOfBeforeProperty);
|
||||
RemoveProperty(nsGkAtoms::animationsOfAfterProperty);
|
||||
RemoveProperty(nsGkAtoms::animationsOfMarkerProperty);
|
||||
RemoveProperty(nsGkAtoms::animationsProperty);
|
||||
if (document) {
|
||||
if (nsPresContext* presContext = document->GetPresContext()) {
|
||||
// We have to clear all pending restyle requests for the animations on
|
||||
@ -2050,9 +2072,7 @@ void Element::SetSMILOverrideStyleDeclaration(DeclarationBlock& aDeclaration) {
|
||||
|
||||
bool Element::IsLabelable() const { return false; }
|
||||
|
||||
bool Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const {
|
||||
return false;
|
||||
}
|
||||
bool Element::IsInteractiveHTMLContent() const { return false; }
|
||||
|
||||
DeclarationBlock* Element::GetInlineStyleDeclaration() const {
|
||||
if (!MayHaveStyle()) {
|
||||
@ -2613,8 +2633,7 @@ bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::exportparts &&
|
||||
StaticPrefs::layout_css_shadow_parts_enabled()) {
|
||||
if (aAttribute == nsGkAtoms::exportparts) {
|
||||
aResult.ParsePartMapping(aValue);
|
||||
return true;
|
||||
}
|
||||
@ -3325,8 +3344,78 @@ CORSMode Element::AttrValueToCORSMode(const nsAttrValue* aValue) {
|
||||
return CORSMode(aValue->GetEnumValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nullptr if requests for fullscreen are allowed in the current
|
||||
* context. Requests are only allowed if the user initiated them (like with
|
||||
* a mouse-click or key press), unless this check has been disabled by
|
||||
* setting the pref "full-screen-api.allow-trusted-requests-only" to false
|
||||
* or if the caller is privileged. Feature policy may also deny requests.
|
||||
* If fullscreen is not allowed, a key for the error message is returned.
|
||||
*/
|
||||
static const char* GetFullscreenError(CallerType aCallerType) {
|
||||
return nsContentUtils::CheckRequestFullscreenAllowed(aCallerType);
|
||||
// Privileged callers can always request fullscreen
|
||||
if (aCallerType == CallerType::System) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure feature policy allows using the fullscreen API
|
||||
/*if (!FeaturePolicyUtils::IsFeatureAllowed(aDocument,
|
||||
NS_LITERAL_STRING("fullscreen"))) {
|
||||
return "FullscreenDeniedFeaturePolicy";
|
||||
}*/
|
||||
|
||||
// Bypass user interaction checks if preference is set
|
||||
if (!StaticPrefs::full_screen_api_allow_trusted_requests_only()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!UserActivation::IsHandlingUserInput()) {
|
||||
return "FullscreenDeniedNotInputDriven";
|
||||
}
|
||||
|
||||
// If more time has elapsed since the user input than is specified by the
|
||||
// dom.event.handling-user-input-time-limit pref (default 1 second),
|
||||
// disallow fullscreen
|
||||
TimeDuration timeout = nsContentUtils::HandlingUserInputTimeout();
|
||||
if (timeout > TimeDuration(nullptr) &&
|
||||
(TimeStamp::Now() - UserActivation::GetHandlingInputStart()) > timeout) {
|
||||
return "FullscreenDeniedNotInputDriven";
|
||||
}
|
||||
|
||||
// Entering full-screen on mouse mouse event is only allowed with left mouse
|
||||
// button
|
||||
if (StaticPrefs::full_screen_api_mouse_event_allow_left_button_only() &&
|
||||
(EventStateManager::sCurrentMouseBtn == MouseButton::eMiddle ||
|
||||
EventStateManager::sCurrentMouseBtn == MouseButton::eRight)) {
|
||||
return "FullscreenDeniedMouseEventOnlyLeftBtn";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Element::SetCapture(bool aRetargetToElement) {
|
||||
// If there is already an active capture, ignore this request. This would
|
||||
// occur if a splitter, frame resizer, etc had already captured and we don't
|
||||
// want to override those.
|
||||
if (!PresShell::GetCapturingContent()) {
|
||||
PresShell::SetCapturingContent(
|
||||
this, CaptureFlags::PreventDragStart |
|
||||
(aRetargetToElement ? CaptureFlags::RetargetToElement
|
||||
: CaptureFlags::None));
|
||||
}
|
||||
}
|
||||
|
||||
void Element::SetCaptureAlways(bool aRetargetToElement) {
|
||||
PresShell::SetCapturingContent(
|
||||
this, CaptureFlags::PreventDragStart | CaptureFlags::IgnoreAllowedState |
|
||||
(aRetargetToElement ? CaptureFlags::RetargetToElement
|
||||
: CaptureFlags::None));
|
||||
}
|
||||
|
||||
void Element::ReleaseCapture() {
|
||||
if (PresShell::GetCapturingContent() == this) {
|
||||
PresShell::ReleaseCapturingContent();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
|
||||
@ -3334,12 +3423,6 @@ already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
|
||||
auto request = FullscreenRequest::Create(this, aCallerType, aRv);
|
||||
RefPtr<Promise> promise = request->GetPromise();
|
||||
|
||||
if (!FeaturePolicyUtils::IsFeatureAllowed(OwnerDoc(),
|
||||
NS_LITERAL_STRING("fullscreen"))) {
|
||||
request->Reject("FullscreenDeniedFeaturePolicy");
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Only grant fullscreen requests if this is called from inside a trusted
|
||||
// event handler (i.e. inside an event handler for a user initiated event).
|
||||
// This stops the fullscreen from being abused similar to the popups of old,
|
||||
@ -3360,12 +3443,11 @@ void Element::RequestPointerLock(CallerType aCallerType) {
|
||||
}
|
||||
|
||||
already_AddRefed<Flex> Element::GetAsFlexContainer() {
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
|
||||
// We need the flex frame to compute additional info, and use
|
||||
// that annotated version of the frame.
|
||||
nsFlexContainerFrame* flexFrame =
|
||||
nsFlexContainerFrame::GetFlexFrameWithComputedInfo(frame);
|
||||
nsFlexContainerFrame::GetFlexFrameWithComputedInfo(
|
||||
GetPrimaryFrame(FlushType::Layout));
|
||||
|
||||
if (flexFrame) {
|
||||
RefPtr<Flex> flex = new Flex(this, flexFrame);
|
||||
@ -3376,7 +3458,8 @@ already_AddRefed<Flex> Element::GetAsFlexContainer() {
|
||||
|
||||
void Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult) {
|
||||
nsGridContainerFrame* frame =
|
||||
nsGridContainerFrame::GetGridFrameWithComputedInfo(GetPrimaryFrame());
|
||||
nsGridContainerFrame::GetGridFrameWithComputedInfo(
|
||||
GetPrimaryFrame(FlushType::Layout));
|
||||
|
||||
// If we get a nsGridContainerFrame from the prior call,
|
||||
// all the next-in-flow frames will also be nsGridContainerFrames.
|
||||
@ -3442,29 +3525,7 @@ already_AddRefed<Animation> Element::Animate(
|
||||
JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError) {
|
||||
Nullable<ElementOrCSSPseudoElement> target;
|
||||
target.SetValue().SetAsElement() = this;
|
||||
return Animate(target, aContext, aKeyframes, aOptions, aError);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Animation> Element::Animate(
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget, JSContext* aContext,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError) {
|
||||
MOZ_ASSERT(!aTarget.IsNull() && (aTarget.Value().IsElement() ||
|
||||
aTarget.Value().IsCSSPseudoElement()),
|
||||
"aTarget should be initialized");
|
||||
|
||||
RefPtr<Element> referenceElement;
|
||||
if (aTarget.Value().IsElement()) {
|
||||
referenceElement = &aTarget.Value().GetAsElement();
|
||||
} else {
|
||||
referenceElement = aTarget.Value().GetAsCSSPseudoElement().Element();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> ownerGlobal = referenceElement->GetOwnerGlobal();
|
||||
nsCOMPtr<nsIGlobalObject> ownerGlobal = GetOwnerGlobal();
|
||||
if (!ownerGlobal) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
@ -3476,8 +3537,8 @@ already_AddRefed<Animation> Element::Animate(
|
||||
// convention and needs to be called in caller's compartment.
|
||||
// This should match to RunConstructorInCallerCompartment attribute in
|
||||
// KeyframeEffect.webidl.
|
||||
RefPtr<KeyframeEffect> effect = KeyframeEffect::Constructor(
|
||||
global, aTarget, aKeyframes, aOptions, aError);
|
||||
RefPtr<KeyframeEffect> effect =
|
||||
KeyframeEffect::Constructor(global, this, aKeyframes, aOptions, aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -3486,7 +3547,7 @@ already_AddRefed<Animation> Element::Animate(
|
||||
// needs to be called in the target element's realm.
|
||||
JSAutoRealm ar(aContext, global.Get());
|
||||
|
||||
AnimationTimeline* timeline = referenceElement->OwnerDoc()->Timeline();
|
||||
AnimationTimeline* timeline = OwnerDoc()->Timeline();
|
||||
RefPtr<Animation> animation = Animation::Constructor(
|
||||
global, effect, Optional<AnimationTimeline*>(timeline), aError);
|
||||
if (aError.Failed()) {
|
||||
@ -3506,23 +3567,26 @@ already_AddRefed<Animation> Element::Animate(
|
||||
}
|
||||
|
||||
void Element::GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations,
|
||||
Flush aFlush) {
|
||||
if (aFlush == Flush::Yes) {
|
||||
if (Document* doc = GetComposedDoc()) {
|
||||
// We don't need to explicitly flush throttled animations here, since
|
||||
// updating the animation style of elements will never affect the set of
|
||||
// running animations and it's only the set of running animations that is
|
||||
// important here.
|
||||
//
|
||||
// NOTE: Any changes to the flags passed to the following call should
|
||||
// be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations
|
||||
// too.
|
||||
doc->FlushPendingNotifications(
|
||||
ChangesToFlush(FlushType::Style, false /* flush animations */));
|
||||
}
|
||||
nsTArray<RefPtr<Animation>>& aAnimations) {
|
||||
if (Document* doc = GetComposedDoc()) {
|
||||
// We don't need to explicitly flush throttled animations here, since
|
||||
// updating the animation style of elements will never affect the set of
|
||||
// running animations and it's only the set of running animations that is
|
||||
// important here.
|
||||
//
|
||||
// NOTE: Any changes to the flags passed to the following call should
|
||||
// be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations
|
||||
// too.
|
||||
doc->FlushPendingNotifications(
|
||||
ChangesToFlush(FlushType::Style, false /* flush animations */));
|
||||
}
|
||||
|
||||
GetAnimationsWithoutFlush(aOptions, aAnimations);
|
||||
}
|
||||
|
||||
void Element::GetAnimationsWithoutFlush(
|
||||
const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations) {
|
||||
Element* elem = this;
|
||||
PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
|
||||
// For animations on generated-content elements, the animations are stored
|
||||
@ -3632,8 +3696,8 @@ void Element::SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError) {
|
||||
localName = nsGkAtoms::body;
|
||||
namespaceID = kNameSpaceID_XHTML;
|
||||
}
|
||||
RefPtr<DocumentFragment> fragment =
|
||||
new DocumentFragment(OwnerDoc()->NodeInfoManager());
|
||||
RefPtr<DocumentFragment> fragment = new (OwnerDoc()->NodeInfoManager())
|
||||
DocumentFragment(OwnerDoc()->NodeInfoManager());
|
||||
nsContentUtils::ParseFragmentHTML(
|
||||
aOuterHTML, fragment, localName, namespaceID,
|
||||
OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
|
||||
@ -3869,15 +3933,32 @@ float Element::FontSizeInflation() {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
ReferrerPolicy Element::GetReferrerPolicyAsEnum() {
|
||||
void Element::GetImplementedPseudoElement(nsAString& aPseudo) const {
|
||||
PseudoStyleType pseudoType = GetPseudoElementType();
|
||||
if (pseudoType == PseudoStyleType::NotPseudo) {
|
||||
return SetDOMStringToNull(aPseudo);
|
||||
}
|
||||
nsDependentAtomString pseudo(nsCSSPseudoElements::GetPseudoAtom(pseudoType));
|
||||
|
||||
// We want to use the modern syntax (::placeholder, etc), but the atoms only
|
||||
// contain one semi-colon.
|
||||
MOZ_ASSERT(pseudo.Length() > 2 && pseudo[0] == ':' && pseudo[1] != ':');
|
||||
|
||||
aPseudo.Truncate();
|
||||
aPseudo.SetCapacity(pseudo.Length() + 1);
|
||||
aPseudo.Append(':');
|
||||
aPseudo.Append(pseudo);
|
||||
}
|
||||
|
||||
ReferrerPolicy Element::GetReferrerPolicyAsEnum() const {
|
||||
if (IsHTMLElement()) {
|
||||
const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrerpolicy);
|
||||
return ReferrerPolicyFromAttr(referrerValue);
|
||||
return ReferrerPolicyFromAttr(GetParsedAttr(nsGkAtoms::referrerpolicy));
|
||||
}
|
||||
return ReferrerPolicy::_empty;
|
||||
}
|
||||
|
||||
ReferrerPolicy Element::ReferrerPolicyFromAttr(const nsAttrValue* aValue) {
|
||||
ReferrerPolicy Element::ReferrerPolicyFromAttr(
|
||||
const nsAttrValue* aValue) const {
|
||||
if (aValue && aValue->Type() == nsAttrValue::eEnum) {
|
||||
return ReferrerPolicy(aValue->GetEnumValue());
|
||||
}
|
||||
@ -3952,14 +4033,14 @@ void Element::UnregisterIntersectionObserver(
|
||||
if (observers) {
|
||||
observers->Remove(aObserver);
|
||||
if (observers->IsEmpty()) {
|
||||
DeleteProperty(nsGkAtoms::intersectionobserverlist);
|
||||
RemoveProperty(nsGkAtoms::intersectionobserverlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Element::UnlinkIntersectionObservers() {
|
||||
// IntersectionObserverPropertyDtor takes care of the hard work.
|
||||
DeleteProperty(nsGkAtoms::intersectionobserverlist);
|
||||
RemoveProperty(nsGkAtoms::intersectionobserverlist);
|
||||
}
|
||||
|
||||
bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver,
|
||||
|
@ -17,16 +17,15 @@
|
||||
#include "nsChangeHint.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsINodeList.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "Units.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/FlushType.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/PseudoStyleType.h"
|
||||
#include "mozilla/RustCell.h"
|
||||
#include "mozilla/SMILAttr.h"
|
||||
@ -162,7 +161,7 @@ class Element : public FragmentOrElement {
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
explicit Element(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
||||
: FragmentOrElement(std::move(aNodeInfo)),
|
||||
mState(NS_EVENT_STATE_MOZ_READONLY | NS_EVENT_STATE_DEFINED) {
|
||||
mState(NS_EVENT_STATE_READONLY | NS_EVENT_STATE_DEFINED) {
|
||||
MOZ_ASSERT(mNodeInfo->NodeType() == ELEMENT_NODE,
|
||||
"Bad NodeType in aNodeInfo");
|
||||
SetIsElement();
|
||||
@ -216,6 +215,11 @@ class Element : public FragmentOrElement {
|
||||
*/
|
||||
int32_t TabIndex();
|
||||
|
||||
/**
|
||||
* Get the parsed value of tabindex attribute.
|
||||
*/
|
||||
Maybe<int32_t> GetTabIndexAttrValue();
|
||||
|
||||
/**
|
||||
* Set tabIndex value to this element.
|
||||
*/
|
||||
@ -378,7 +382,7 @@ class Element : public FragmentOrElement {
|
||||
/**
|
||||
* Returns if the element is interactive content as per HTML specification.
|
||||
*/
|
||||
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const;
|
||||
virtual bool IsInteractiveHTMLContent() const;
|
||||
|
||||
/**
|
||||
* Returns |this| as an nsIMozBrowserFrame* if the element is a frame or
|
||||
@ -1180,31 +1184,11 @@ class Element : public FragmentOrElement {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void SetCapture(bool aRetargetToElement) {
|
||||
// If there is already an active capture, ignore this request. This would
|
||||
// occur if a splitter, frame resizer, etc had already captured and we don't
|
||||
// want to override those.
|
||||
if (!PresShell::GetCapturingContent()) {
|
||||
PresShell::SetCapturingContent(
|
||||
this, CaptureFlags::PreventDragStart |
|
||||
(aRetargetToElement ? CaptureFlags::RetargetToElement
|
||||
: CaptureFlags::None));
|
||||
}
|
||||
}
|
||||
void SetCapture(bool aRetargetToElement);
|
||||
|
||||
void SetCaptureAlways(bool aRetargetToElement) {
|
||||
PresShell::SetCapturingContent(
|
||||
this, CaptureFlags::PreventDragStart |
|
||||
CaptureFlags::IgnoreAllowedState |
|
||||
(aRetargetToElement ? CaptureFlags::RetargetToElement
|
||||
: CaptureFlags::None));
|
||||
}
|
||||
void SetCaptureAlways(bool aRetargetToElement);
|
||||
|
||||
void ReleaseCapture() {
|
||||
if (PresShell::GetCapturingContent() == this) {
|
||||
PresShell::ReleaseCapturingContent();
|
||||
}
|
||||
}
|
||||
void ReleaseCapture();
|
||||
|
||||
already_AddRefed<Promise> RequestFullscreen(CallerType, ErrorResult&);
|
||||
void RequestPointerLock(CallerType aCallerType);
|
||||
@ -1275,48 +1259,52 @@ class Element : public FragmentOrElement {
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ScrollHeight();
|
||||
MOZ_CAN_RUN_SCRIPT void MozScrollSnap();
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ClientTop() {
|
||||
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().y);
|
||||
return CSSPixel::FromAppUnits(GetClientAreaRect().y).Rounded();
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ClientLeft() {
|
||||
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().x);
|
||||
return CSSPixel::FromAppUnits(GetClientAreaRect().x).Rounded();
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ClientWidth() {
|
||||
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().Width());
|
||||
return CSSPixel::FromAppUnits(GetClientAreaRect().Width()).Rounded();
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ClientHeight() {
|
||||
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().Height());
|
||||
return CSSPixel::FromAppUnits(GetClientAreaRect().Height()).Rounded();
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ScrollTopMin() {
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().y)
|
||||
: 0;
|
||||
if (!sf) {
|
||||
return 0;
|
||||
}
|
||||
return CSSPixel::FromAppUnits(sf->GetScrollRange().y).Rounded();
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ScrollTopMax() {
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? nsPresContext::AppUnitsToIntCSSPixels(
|
||||
sf->GetScrollRange().YMost())
|
||||
: 0;
|
||||
if (!sf) {
|
||||
return 0;
|
||||
}
|
||||
return CSSPixel::FromAppUnits(sf->GetScrollRange().YMost()).Rounded();
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMin() {
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().x)
|
||||
: 0;
|
||||
if (!sf) {
|
||||
return 0;
|
||||
}
|
||||
return CSSPixel::FromAppUnits(sf->GetScrollRange().x).Rounded();
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT int32_t ScrollLeftMax() {
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? nsPresContext::AppUnitsToIntCSSPixels(
|
||||
sf->GetScrollRange().XMost())
|
||||
: 0;
|
||||
if (!sf) {
|
||||
return 0;
|
||||
}
|
||||
return CSSPixel::FromAppUnits(sf->GetScrollRange().XMost()).Rounded();
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT double ClientHeightDouble() {
|
||||
return nsPresContext::AppUnitsToDoubleCSSPixels(
|
||||
GetClientAreaRect().Height());
|
||||
return CSSPixel::FromAppUnits(GetClientAreaRect().Height());
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT double ClientWidthDouble() {
|
||||
return nsPresContext::AppUnitsToDoubleCSSPixels(
|
||||
GetClientAreaRect().Width());
|
||||
return CSSPixel::FromAppUnits(GetClientAreaRect().Width());
|
||||
}
|
||||
|
||||
// This function will return the block size of first line box, no matter if
|
||||
@ -1337,20 +1325,12 @@ class Element : public FragmentOrElement {
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError);
|
||||
|
||||
// A helper method that factors out the common functionality needed by
|
||||
// Element::Animate and CSSPseudoElement::Animate
|
||||
static already_AddRefed<Animation> Animate(
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget, JSContext* aContext,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError);
|
||||
|
||||
enum class Flush { Yes, No };
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations,
|
||||
Flush aFlush = Flush::Yes);
|
||||
nsTArray<RefPtr<Animation>>& aAnimations);
|
||||
|
||||
void GetAnimationsWithoutFlush(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations);
|
||||
|
||||
static void GetAnimationsUnsorted(Element* aElement,
|
||||
PseudoStyleType aPseudoType,
|
||||
@ -1588,8 +1568,10 @@ class Element : public FragmentOrElement {
|
||||
*/
|
||||
float FontSizeInflation();
|
||||
|
||||
ReferrerPolicy GetReferrerPolicyAsEnum();
|
||||
ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue);
|
||||
void GetImplementedPseudoElement(nsAString&) const;
|
||||
|
||||
ReferrerPolicy GetReferrerPolicyAsEnum() const;
|
||||
ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue) const;
|
||||
|
||||
/*
|
||||
* Helpers for .dataset. This is implemented on Element, though only some
|
||||
@ -2039,6 +2021,11 @@ inline mozilla::dom::Element* nsINode::GetPreviousElementSibling() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline mozilla::dom::Element* nsINode::GetAsElementOrParentElement() const {
|
||||
return IsElement() ? const_cast<mozilla::dom::Element*>(AsElement())
|
||||
: GetParentElement();
|
||||
}
|
||||
|
||||
inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
|
||||
nsIContent* nextSibling = GetNextSibling();
|
||||
while (nextSibling) {
|
||||
@ -2060,7 +2047,8 @@ inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
|
||||
nsINode** aResult) const { \
|
||||
*aResult = nullptr; \
|
||||
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo); \
|
||||
RefPtr<_elementName> it = new _elementName(ni.forget()); \
|
||||
auto* nim = ni->NodeInfoManager(); \
|
||||
RefPtr<_elementName> it = new (nim) _elementName(ni.forget()); \
|
||||
nsresult rv = const_cast<_elementName*>(this)->CopyInnerTo(it); \
|
||||
if (NS_SUCCEEDED(rv)) { \
|
||||
it.forget(aResult); \
|
||||
@ -2075,8 +2063,9 @@ inline mozilla::dom::Element* nsINode::GetNextElementSibling() const {
|
||||
nsINode** aResult) const { \
|
||||
*aResult = nullptr; \
|
||||
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo); \
|
||||
auto* nim = ni->NodeInfoManager(); \
|
||||
RefPtr<_elementName> it = \
|
||||
new _elementName(ni.forget() EXPAND extra_args_); \
|
||||
new (nim) _elementName(ni.forget() EXPAND extra_args_); \
|
||||
nsresult rv = it->Init(); \
|
||||
nsresult rv2 = const_cast<_elementName*>(this)->CopyInnerTo(it); \
|
||||
if (NS_FAILED(rv2)) { \
|
||||
|
@ -929,10 +929,9 @@ void EventSourceImpl::SetupHttpChannel() {
|
||||
nsresult EventSourceImpl::SetupReferrerInfo() {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!IsShutDown());
|
||||
nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent();
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
|
||||
referrerInfo->InitWithDocument(doc);
|
||||
|
||||
if (nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent()) {
|
||||
auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
|
||||
nsresult rv = mHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
@ -1781,7 +1780,7 @@ EventSource::EventSource(nsIGlobalObject* aGlobal,
|
||||
mImpl = new EventSourceImpl(this, aCookieSettings);
|
||||
}
|
||||
|
||||
EventSource::~EventSource() {}
|
||||
EventSource::~EventSource() = default;
|
||||
|
||||
nsresult EventSource::CreateAndDispatchSimpleEvent(const nsAString& aName) {
|
||||
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
|
||||
|
57
dom/base/FilteredNodeIterator.h
Normal file
57
dom/base/FilteredNodeIterator.h
Normal 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
|
@ -15,14 +15,13 @@ using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
FormData::FormData(nsISupports* aOwner, NotNull<const Encoding*> aEncoding,
|
||||
Element* aOriginatingElement)
|
||||
: HTMLFormSubmission(nullptr, EmptyString(), aEncoding,
|
||||
aOriginatingElement),
|
||||
Element* aSubmitter)
|
||||
: HTMLFormSubmission(nullptr, EmptyString(), aEncoding, aSubmitter),
|
||||
mOwner(aOwner) {}
|
||||
|
||||
FormData::FormData(const FormData& aFormData)
|
||||
: HTMLFormSubmission(aFormData.mActionURL, aFormData.mTarget,
|
||||
aFormData.mEncoding, aFormData.mOriginatingElement) {
|
||||
aFormData.mEncoding, aFormData.mSubmitter) {
|
||||
mOwner = aFormData.mOwner;
|
||||
mFormData = aFormData.mFormData;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class FormData final : public nsISupports,
|
||||
public nsWrapperCache {
|
||||
private:
|
||||
FormData(const FormData& aFormData);
|
||||
~FormData() {}
|
||||
~FormData() = default;
|
||||
|
||||
struct FormDataTuple {
|
||||
nsString name;
|
||||
@ -50,7 +50,7 @@ class FormData final : public nsISupports,
|
||||
public:
|
||||
explicit FormData(nsISupports* aOwner = nullptr,
|
||||
NotNull<const Encoding*> aEncoding = UTF_8_ENCODING,
|
||||
Element* aOriginatingElement = nullptr);
|
||||
Element* aSubmitter = nullptr);
|
||||
|
||||
already_AddRefed<FormData> Clone();
|
||||
|
||||
|
@ -146,8 +146,11 @@ NS_INTERFACE_MAP_BEGIN(nsIContent)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsIContent)
|
||||
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
|
||||
nsIContent, LastRelease())
|
||||
|
||||
NS_IMPL_DOMARENA_DESTROY(nsIContent)
|
||||
|
||||
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(
|
||||
nsIContent, LastRelease(), Destroy())
|
||||
|
||||
nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
|
||||
// This handles also nested native anonymous content.
|
||||
@ -196,7 +199,7 @@ nsIContent::IMEState nsIContent::GetDesiredIMEState() {
|
||||
// Check for the special case where we're dealing with elements which don't
|
||||
// have the editable flag set, but are readwrite (such as text controls).
|
||||
if (!IsElement() ||
|
||||
!AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
|
||||
!AsElement()->State().HasState(NS_EVENT_STATE_READWRITE)) {
|
||||
return IMEState(IMEState::DISABLED);
|
||||
}
|
||||
}
|
||||
@ -226,7 +229,7 @@ nsIContent::IMEState nsIContent::GetDesiredIMEState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
bool nsIContent::HasIndependentSelection() {
|
||||
bool nsIContent::HasIndependentSelection() const {
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
|
||||
}
|
||||
@ -279,22 +282,18 @@ nsresult nsIContent::LookupNamespaceURIInternal(
|
||||
}
|
||||
// Trace up the content parent chain looking for the namespace
|
||||
// declaration that declares aNamespacePrefix.
|
||||
const nsIContent* content = this;
|
||||
do {
|
||||
if (content->IsElement() &&
|
||||
content->AsElement()->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
|
||||
for (Element* element = GetAsElementOrParentElement(); element;
|
||||
element = element->GetParentElement()) {
|
||||
if (element->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) {
|
||||
return NS_OK;
|
||||
} while ((content = content->GetParent()));
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAtom* nsIContent::GetLang() const {
|
||||
for (const auto* content = this; content; content = content->GetParent()) {
|
||||
if (!content->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* element = content->AsElement();
|
||||
for (const Element* element = GetAsElementOrParentElement(); element;
|
||||
element = element->GetParentElement()) {
|
||||
if (!element->GetAttrCount()) {
|
||||
continue;
|
||||
}
|
||||
@ -1325,14 +1324,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
|
||||
nsStaticAtom* const* props =
|
||||
Element::HTMLSVGPropertiesToTraverseAndUnlink();
|
||||
for (uint32_t i = 0; props[i]; ++i) {
|
||||
tmp->DeleteProperty(props[i]);
|
||||
tmp->RemoveProperty(props[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp->MayHaveAnimations()) {
|
||||
nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
|
||||
for (uint32_t i = 0; effectProps[i]; ++i) {
|
||||
tmp->DeleteProperty(effectProps[i]);
|
||||
tmp->RemoveProperty(effectProps[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1794,12 +1793,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
|
||||
orphan.AppendLiteral(" (orphan)");
|
||||
}
|
||||
|
||||
static const char* kNSURIs[] = {" ([none])", " (xmlns)", " (xml)",
|
||||
" (xhtml)", " (XLink)", " (XSLT)",
|
||||
" (XBL)", " (MathML)", " (RDF)",
|
||||
" (XUL)", " (SVG)", " (XML Events)"};
|
||||
const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
|
||||
SprintfLiteral(name, "FragmentOrElement%s %s%s%s%s %s", nsuri,
|
||||
const char* nsuri = nsNameSpaceManager::GetNameSpaceDisplayName(nsid);
|
||||
SprintfLiteral(name, "FragmentOrElement %s %s%s%s%s %s", nsuri,
|
||||
localName.get(), NS_ConvertUTF16toUTF8(id).get(),
|
||||
NS_ConvertUTF16toUTF8(classes).get(), orphan.get(),
|
||||
uri.get());
|
||||
@ -2048,16 +2043,17 @@ void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
|
||||
|
||||
nsAutoScriptLoaderDisabler sld(doc);
|
||||
|
||||
nsAtom* contextLocalName = NodeInfo()->NameAtom();
|
||||
int32_t contextNameSpaceID = GetNameSpaceID();
|
||||
|
||||
FragmentOrElement* parseContext = this;
|
||||
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
|
||||
// Fix up the context to be the host of the ShadowRoot.
|
||||
contextLocalName = shadowRoot->GetHost()->NodeInfo()->NameAtom();
|
||||
contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
|
||||
// Fix up the context to be the host of the ShadowRoot. See
|
||||
// https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1.
|
||||
parseContext = shadowRoot->GetHost();
|
||||
}
|
||||
|
||||
if (doc->IsHTMLDocument()) {
|
||||
nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom();
|
||||
int32_t contextNameSpaceID = parseContext->GetNameSpaceID();
|
||||
|
||||
int32_t oldChildCount = target->GetChildCount();
|
||||
aError = nsContentUtils::ParseFragmentHTML(
|
||||
aInnerHTML, target, contextLocalName, contextNameSpaceID,
|
||||
@ -2068,14 +2064,14 @@ void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
|
||||
oldChildCount);
|
||||
} else {
|
||||
RefPtr<DocumentFragment> df = nsContentUtils::CreateContextualFragment(
|
||||
target, aInnerHTML, true, aError);
|
||||
parseContext, aInnerHTML, true, aError);
|
||||
if (!aError.Failed()) {
|
||||
// Suppress assertion about node removal mutation events that can't have
|
||||
// listeners anyway, because no one has had the chance to register
|
||||
// mutation listeners on the fragment that comes from the parser.
|
||||
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
|
||||
|
||||
static_cast<nsINode*>(target)->AppendChild(*df, aError);
|
||||
target->AppendChild(*df, aError);
|
||||
mb.NodesAdded();
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class nsNodeSupportsWeakRefTearoff final : public nsISupportsWeakReference {
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsNodeSupportsWeakRefTearoff)
|
||||
|
||||
private:
|
||||
~nsNodeSupportsWeakRefTearoff() {}
|
||||
~nsNodeSupportsWeakRefTearoff() = default;
|
||||
|
||||
nsCOMPtr<nsINode> mNode;
|
||||
};
|
||||
|
@ -39,12 +39,16 @@ class FullscreenChange : public LinkedListElement<FullscreenChange> {
|
||||
}
|
||||
}
|
||||
|
||||
void MayRejectPromise() const {
|
||||
void MayRejectPromise(const nsACString& aMessage) {
|
||||
if (mPromise) {
|
||||
MOZ_ASSERT(mPromise->State() == Promise::PromiseState::Pending);
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
|
||||
mPromise->MaybeRejectWithTypeError(aMessage);
|
||||
}
|
||||
}
|
||||
template <int N>
|
||||
void MayRejectPromise(const char (&aMessage)[N]) {
|
||||
MayRejectPromise(nsLiteralCString(aMessage));
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef dom::Promise Promise;
|
||||
@ -89,14 +93,14 @@ class FullscreenRequest : public FullscreenChange {
|
||||
|
||||
// Reject the fullscreen request with the given reason.
|
||||
// It will dispatch the fullscreenerror event.
|
||||
void Reject(const char* aReason) const {
|
||||
void Reject(const char* aReason) {
|
||||
if (nsPresContext* presContext = Document()->GetPresContext()) {
|
||||
auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
|
||||
FullscreenEventType::Error, Document(), mElement);
|
||||
presContext->RefreshDriver()->ScheduleFullscreenEvent(
|
||||
std::move(pendingEvent));
|
||||
}
|
||||
MayRejectPromise();
|
||||
MayRejectPromise("Fullscreen request denied");
|
||||
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
||||
NS_LITERAL_CSTRING("DOM"), Document(),
|
||||
nsContentUtils::eDOM_PROPERTIES, aReason);
|
||||
@ -143,7 +147,7 @@ class FullscreenExit : public FullscreenChange {
|
||||
return WrapUnique(new FullscreenExit(aDoc, nullptr));
|
||||
}
|
||||
|
||||
~FullscreenExit() { MOZ_COUNT_DTOR(FullscreenExit); }
|
||||
MOZ_COUNTED_DTOR(FullscreenExit)
|
||||
|
||||
private:
|
||||
FullscreenExit(dom::Document* aDoc, already_AddRefed<Promise> aPromise)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user