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