mirror of
https://github.com/Feodor2/Mypal68.git
synced 2025-06-19 07:15:36 -04:00
68.13 - dom
This commit is contained in:
parent
f6176dd28b
commit
adbe3a4a0f
@ -3,19 +3,25 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Animation.h"
|
||||
|
||||
#include "AnimationUtils.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "mozilla/dom/AnimationBinding.h"
|
||||
#include "mozilla/dom/AnimationPlaybackEvent.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/DocumentTimeline.h"
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/AnimationEventDispatcher.h"
|
||||
#include "mozilla/AnimationTarget.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/DeclarationBlock.h"
|
||||
#include "mozilla/Maybe.h" // For Maybe
|
||||
#include "mozilla/TypeTraits.h" // For std::forward<>
|
||||
#include "nsAnimationManager.h" // For CSSAnimation
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
|
||||
#include "nsDOMCSSAttrDeclaration.h" // For nsDOMCSSAttributeDeclaration
|
||||
#include "nsThreadUtils.h" // For nsRunnableMethod and nsRevocableEventPtr
|
||||
#include "nsTransitionManager.h" // For CSSTransition
|
||||
#include "PendingAnimationTracker.h" // For PendingAnimationTracker
|
||||
@ -54,8 +60,7 @@ class MOZ_RAII AutoMutationBatchForAnimation {
|
||||
explicit AutoMutationBatchForAnimation(
|
||||
const Animation& aAnimation MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
Maybe<NonOwningAnimationTarget> target =
|
||||
nsNodeUtils::GetTargetForAnimation(&aAnimation);
|
||||
Maybe<NonOwningAnimationTarget> target = aAnimation.GetTargetForAnimation();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
@ -75,6 +80,15 @@ class MOZ_RAII AutoMutationBatchForAnimation {
|
||||
// Animation interface:
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Maybe<NonOwningAnimationTarget> Animation::GetTargetForAnimation() const {
|
||||
AnimationEffect* effect = GetEffect();
|
||||
if (!effect || !effect->AsKeyframeEffect()) {
|
||||
return Nothing();
|
||||
}
|
||||
return effect->AsKeyframeEffect()->GetTarget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Animation> Animation::Constructor(
|
||||
const GlobalObject& aGlobal, AnimationEffect* aEffect,
|
||||
@ -106,7 +120,7 @@ void Animation::SetId(const nsAString& aId) {
|
||||
return;
|
||||
}
|
||||
mId = aId;
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
|
||||
void Animation::SetEffect(AnimationEffect* aEffect) {
|
||||
@ -129,7 +143,7 @@ void Animation::SetEffectNoUpdate(AnimationEffect* aEffect) {
|
||||
// We need to notify observers now because once we set mEffect to null
|
||||
// we won't be able to find the target element to notify.
|
||||
if (mIsRelevant) {
|
||||
nsNodeUtils::AnimationRemoved(this);
|
||||
MutationObservers::NotifyAnimationRemoved(this);
|
||||
}
|
||||
|
||||
// Break links with the old effect and then drop it.
|
||||
@ -160,12 +174,14 @@ void Animation::SetEffectNoUpdate(AnimationEffect* aEffect) {
|
||||
// If the target is different, the change notification will be ignored by
|
||||
// AutoMutationBatchForAnimation.
|
||||
if (wasRelevant && mIsRelevant) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
|
||||
ReschedulePendingTasks();
|
||||
}
|
||||
|
||||
MaybeScheduleReplacementCheck();
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
@ -244,7 +260,7 @@ void Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime) {
|
||||
|
||||
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
|
||||
if (IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
PostUpdate();
|
||||
}
|
||||
@ -298,7 +314,7 @@ void Animation::SetCurrentTime(const TimeDuration& aSeekTime) {
|
||||
|
||||
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
|
||||
if (IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
PostUpdate();
|
||||
}
|
||||
@ -329,7 +345,7 @@ void Animation::SetPlaybackRate(double aPlaybackRate) {
|
||||
// - update the playback rate on animations on layers.
|
||||
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
|
||||
if (IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
PostUpdate();
|
||||
}
|
||||
@ -374,7 +390,7 @@ void Animation::UpdatePlaybackRate(double aPlaybackRate) {
|
||||
// All we need to do is update observers so that, e.g. DevTools, report the
|
||||
// right information.
|
||||
if (IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
} else if (playState == AnimationPlayState::Finished) {
|
||||
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTimeAsDuration().IsNull(),
|
||||
@ -402,7 +418,7 @@ void Animation::UpdatePlaybackRate(double aPlaybackRate) {
|
||||
// timing.
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
if (IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
PostUpdate();
|
||||
} else {
|
||||
@ -476,6 +492,7 @@ void Animation::Cancel(PostRestyleMode aPostRestyle) {
|
||||
|
||||
if (mFinished) {
|
||||
mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
mFinished->SetSettledPromiseIsHandled();
|
||||
}
|
||||
ResetFinishedPromise();
|
||||
|
||||
@ -553,7 +570,7 @@ void Animation::Finish(ErrorResult& aRv) {
|
||||
}
|
||||
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Sync);
|
||||
if (didChange && IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
PostUpdate();
|
||||
}
|
||||
@ -592,6 +609,124 @@ void Animation::Reverse(ErrorResult& aRv) {
|
||||
// it here.
|
||||
}
|
||||
|
||||
void Animation::Persist() {
|
||||
if (mReplaceState == AnimationReplaceState::Persisted) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasRemoved = mReplaceState == AnimationReplaceState::Removed;
|
||||
|
||||
mReplaceState = AnimationReplaceState::Persisted;
|
||||
|
||||
// If the animation is not (yet) removed, there should be no side effects of
|
||||
// persisting it.
|
||||
if (wasRemoved) {
|
||||
UpdateEffect(PostRestyleMode::IfNeeded);
|
||||
PostUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/web-animations/#dom-animation-commitstyles
|
||||
void Animation::CommitStyles(ErrorResult& aRv) {
|
||||
if (!mEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Take an owning reference to the keyframe effect. This will ensure that
|
||||
// this Animation and the target element remain alive after flushing style.
|
||||
RefPtr<KeyframeEffect> keyframeEffect = mEffect->AsKeyframeEffect();
|
||||
if (!keyframeEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target = keyframeEffect->GetTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->mPseudoType != PseudoStyleType::NotPseudo) {
|
||||
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check it is an element with a style attribute
|
||||
nsCOMPtr<nsStyledElement> styledElement = do_QueryInterface(target->mElement);
|
||||
if (!styledElement) {
|
||||
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Flush style before checking if the target element is rendered since the
|
||||
// result could depend on pending style changes.
|
||||
if (Document* doc = target->mElement->GetComposedDoc()) {
|
||||
doc->FlushPendingNotifications(FlushType::Style);
|
||||
}
|
||||
if (!target->mElement->IsRendered()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(target->mElement);
|
||||
if (!presContext) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the computed animation values
|
||||
UniquePtr<RawServoAnimationValueMap> animationValues =
|
||||
Servo_AnimationValueMap_Create().Consume();
|
||||
if (!presContext->EffectCompositor()->ComposeServoAnimationRuleForEffect(
|
||||
*keyframeEffect, CascadeLevel(), animationValues.get())) {
|
||||
NS_WARNING("Failed to compose animation style to commit");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calling SetCSSDeclaration will trigger attribute setting code.
|
||||
// Start the update now so that the old rule doesn't get used
|
||||
// between when we mutate the declaration and when we set the new
|
||||
// rule.
|
||||
mozAutoDocUpdate autoUpdate(target->mElement->OwnerDoc(), true);
|
||||
|
||||
// Get the inline style to append to
|
||||
RefPtr<DeclarationBlock> declarationBlock;
|
||||
if (auto* existing = target->mElement->GetInlineStyleDeclaration()) {
|
||||
declarationBlock = existing->EnsureMutable();
|
||||
} else {
|
||||
declarationBlock = new DeclarationBlock();
|
||||
declarationBlock->SetDirty();
|
||||
}
|
||||
|
||||
// Prepare the callback
|
||||
MutationClosureData closureData;
|
||||
closureData.mClosure = nsDOMCSSAttributeDeclaration::MutationClosureFunction;
|
||||
closureData.mElement = target->mElement;
|
||||
DeclarationBlockMutationClosure beforeChangeClosure = {
|
||||
nsDOMCSSAttributeDeclaration::MutationClosureFunction,
|
||||
&closureData,
|
||||
};
|
||||
|
||||
// Set the animated styles
|
||||
bool changed = false;
|
||||
nsCSSPropertyIDSet properties = keyframeEffect->GetPropertySet();
|
||||
for (nsCSSPropertyID property : properties) {
|
||||
RefPtr<RawServoAnimationValue> computedValue =
|
||||
Servo_AnimationValueMap_GetValue(animationValues.get(), property)
|
||||
.Consume();
|
||||
if (computedValue) {
|
||||
changed |= Servo_DeclarationBlock_SetPropertyToAnimationValue(
|
||||
declarationBlock->Raw(), computedValue, beforeChangeClosure);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update inline style declaration
|
||||
target->mElement->SetInlineStyleDeclaration(*declarationBlock, closureData);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// JS wrappers for Animation interface:
|
||||
@ -648,7 +783,14 @@ void Animation::Tick() {
|
||||
FinishPendingAt(mTimeline->GetCurrentTimeAsDuration().Value());
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Sync);
|
||||
|
||||
// Check for changes to whether or not this animation is replaceable.
|
||||
bool isReplaceable = IsReplaceable();
|
||||
if (isReplaceable && !mWasReplaceableAtLastTick) {
|
||||
ScheduleReplacementCheck();
|
||||
}
|
||||
mWasReplaceableAtLastTick = isReplaceable;
|
||||
|
||||
if (!mEffect) {
|
||||
return;
|
||||
@ -835,16 +977,129 @@ bool Animation::ShouldBeSynchronizedWithMainThread(
|
||||
|
||||
void Animation::UpdateRelevance() {
|
||||
bool wasRelevant = mIsRelevant;
|
||||
mIsRelevant = HasCurrentEffect() || IsInEffect();
|
||||
mIsRelevant = mReplaceState != AnimationReplaceState::Removed &&
|
||||
(HasCurrentEffect() || IsInEffect());
|
||||
|
||||
// Notify animation observers.
|
||||
if (wasRelevant && !mIsRelevant) {
|
||||
nsNodeUtils::AnimationRemoved(this);
|
||||
MutationObservers::NotifyAnimationRemoved(this);
|
||||
} else if (!wasRelevant && mIsRelevant) {
|
||||
nsNodeUtils::AnimationAdded(this);
|
||||
MutationObservers::NotifyAnimationAdded(this);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool IsMarkupAnimation(T* aAnimation) {
|
||||
return aAnimation && aAnimation->IsTiedToMarkup();
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/web-animations/#replaceable-animation
|
||||
bool Animation::IsReplaceable() const {
|
||||
// We never replace CSS animations or CSS transitions since they are managed
|
||||
// by CSS.
|
||||
if (IsMarkupAnimation(AsCSSAnimation()) ||
|
||||
IsMarkupAnimation(AsCSSTransition())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only finished animations can be replaced.
|
||||
if (PlayState() != AnimationPlayState::Finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Already removed animations cannot be replaced.
|
||||
if (ReplaceState() == AnimationReplaceState::Removed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can only replace an animation if we know that, uninterfered, it would
|
||||
// never start playing again. That excludes any animations on timelines that
|
||||
// aren't monotonically increasing.
|
||||
//
|
||||
// If we don't have any timeline at all, then we can't be in the finished
|
||||
// state (since we need both a resolved start time and current time for that)
|
||||
// and will have already returned false above.
|
||||
//
|
||||
// (However, if it ever does become possible to be finished without a timeline
|
||||
// then we will want to return false here since it probably suggests an
|
||||
// animation being driven directly by script, in which case we can't assume
|
||||
// anything about how they will behave.)
|
||||
if (!GetTimeline() || !GetTimeline()->TracksWallclockTime()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the animation doesn't have an effect then we can't determine if it is
|
||||
// filling or not so just leave it alone.
|
||||
if (!GetEffect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// At the time of writing we only know about KeyframeEffects. If we introduce
|
||||
// other types of effects we will need to decide if they are replaceable or
|
||||
// not.
|
||||
MOZ_ASSERT(GetEffect()->AsKeyframeEffect(),
|
||||
"Effect should be a keyframe effect");
|
||||
|
||||
// We only replace animations that are filling.
|
||||
if (GetEffect()->GetComputedTiming().mProgress.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should only replace animations with a target element (since otherwise
|
||||
// what other effects would we consider when determining if they are covered
|
||||
// or not?).
|
||||
if (!GetEffect()->AsKeyframeEffect()->GetTarget()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Animation::IsRemovable() const {
|
||||
return ReplaceState() == AnimationReplaceState::Active && IsReplaceable();
|
||||
}
|
||||
|
||||
void Animation::ScheduleReplacementCheck() {
|
||||
MOZ_ASSERT(
|
||||
IsReplaceable(),
|
||||
"Should only schedule a replacement check for a replaceable animation");
|
||||
|
||||
// If IsReplaceable() is true, the following should also hold
|
||||
MOZ_ASSERT(GetEffect());
|
||||
MOZ_ASSERT(GetEffect()->AsKeyframeEffect());
|
||||
MOZ_ASSERT(GetEffect()->AsKeyframeEffect()->GetTarget());
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target =
|
||||
GetEffect()->AsKeyframeEffect()->GetTarget();
|
||||
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(target->mElement);
|
||||
if (presContext) {
|
||||
presContext->EffectCompositor()->NoteElementForReducing(*target);
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::MaybeScheduleReplacementCheck() {
|
||||
if (!IsReplaceable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScheduleReplacementCheck();
|
||||
}
|
||||
|
||||
void Animation::Remove() {
|
||||
MOZ_ASSERT(IsRemovable(),
|
||||
"Should not be trying to remove an effect that is not removable");
|
||||
|
||||
mReplaceState = AnimationReplaceState::Removed;
|
||||
|
||||
UpdateEffect(PostRestyleMode::IfNeeded);
|
||||
PostUpdate();
|
||||
|
||||
QueuePlaybackEvent(NS_LITERAL_STRING("remove"),
|
||||
GetTimelineCurrentTimeAsTimeStamp());
|
||||
}
|
||||
|
||||
bool Animation::HasLowerCompositeOrderThan(const Animation& aOther) const {
|
||||
// 0. Object-equality case
|
||||
if (&aOther == this) {
|
||||
@ -987,11 +1242,27 @@ void Animation::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
|
||||
|
||||
void Animation::NotifyEffectTimingUpdated() {
|
||||
MOZ_ASSERT(mEffect,
|
||||
"We should only update timing effect when we have a target "
|
||||
"We should only update effect timing when we have a target "
|
||||
"effect");
|
||||
UpdateTiming(Animation::SeekFlag::NoSeek, Animation::SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
void Animation::NotifyEffectPropertiesUpdated() {
|
||||
MOZ_ASSERT(mEffect,
|
||||
"We should only update effect properties when we have a target "
|
||||
"effect");
|
||||
|
||||
MaybeScheduleReplacementCheck();
|
||||
}
|
||||
|
||||
void Animation::NotifyEffectTargetUpdated() {
|
||||
MOZ_ASSERT(mEffect,
|
||||
"We should only update the effect target when we have a target "
|
||||
"effect");
|
||||
|
||||
MaybeScheduleReplacementCheck();
|
||||
}
|
||||
|
||||
void Animation::NotifyGeometricAnimationsStartingThisFrame() {
|
||||
if (!IsNewlyStarted() || !mEffect) {
|
||||
return;
|
||||
@ -1086,7 +1357,7 @@ void Animation::PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior) {
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
if (IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1134,7 +1405,7 @@ void Animation::Pause(ErrorResult& aRv) {
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
if (IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
|
||||
PostUpdate();
|
||||
@ -1177,12 +1448,12 @@ void Animation::ResumeAt(const TimeDuration& aReadyTime) {
|
||||
|
||||
mPendingState = PendingState::NotPending;
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Sync);
|
||||
|
||||
// If we had a pending playback rate, we will have now applied it so we need
|
||||
// to notify observers.
|
||||
if (hadPendingPlaybackRate && IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
MutationObservers::NotifyAnimationChanged(this);
|
||||
}
|
||||
|
||||
if (mReady) {
|
||||
@ -1330,6 +1601,7 @@ void Animation::ResetPendingTasks() {
|
||||
|
||||
if (mReady) {
|
||||
mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
mReady->SetSettledPromiseIsHandled();
|
||||
mReady = nullptr;
|
||||
}
|
||||
}
|
||||
@ -1501,7 +1773,7 @@ void Animation::QueuePlaybackEvent(const nsAString& aName,
|
||||
|
||||
AnimationPlaybackEventInit init;
|
||||
|
||||
if (aName.EqualsLiteral("finish")) {
|
||||
if (aName.EqualsLiteral("finish") || aName.EqualsLiteral("remove")) {
|
||||
init.mCurrentTime = GetCurrentTimeAsDouble();
|
||||
}
|
||||
if (mTimeline) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/PostRestyleMode.h"
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
|
||||
#include "mozilla/dom/AnimationBinding.h" // for AnimationPlayState
|
||||
@ -49,20 +50,19 @@ class Animation : public DOMEventTargetHelper,
|
||||
|
||||
public:
|
||||
explicit Animation(nsIGlobalObject* aGlobal)
|
||||
: DOMEventTargetHelper(aGlobal),
|
||||
mPlaybackRate(1.0),
|
||||
mAnimationIndex(sNextAnimationIndex++),
|
||||
mCachedChildIndex(-1),
|
||||
mPendingState(PendingState::NotPending),
|
||||
mFinishedAtLastComposeStyle(false),
|
||||
mIsRelevant(false),
|
||||
mFinishedIsResolved(false),
|
||||
mSyncWithGeometricAnimations(false) {}
|
||||
: DOMEventTargetHelper(aGlobal), mAnimationIndex(sNextAnimationIndex++) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Animation, DOMEventTargetHelper)
|
||||
|
||||
nsIGlobalObject* GetParentObject() const { return GetOwnerGlobal(); }
|
||||
|
||||
/**
|
||||
* Utility function to get the target (pseudo-)element associated with an
|
||||
* animation.
|
||||
*/
|
||||
Maybe<NonOwningAnimationTarget> GetTargetForAnimation() const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
@ -118,12 +118,14 @@ class Animation : public DOMEventTargetHelper,
|
||||
|
||||
bool Pending() const { return mPendingState != PendingState::NotPending; }
|
||||
virtual bool PendingFromJS() const { return Pending(); }
|
||||
AnimationReplaceState ReplaceState() const { return mReplaceState; }
|
||||
|
||||
virtual Promise* GetReady(ErrorResult& aRv);
|
||||
Promise* GetFinished(ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(finish);
|
||||
IMPL_EVENT_HANDLER(cancel);
|
||||
IMPL_EVENT_HANDLER(remove);
|
||||
|
||||
void Cancel(PostRestyleMode aPostRestyle = PostRestyleMode::IfNeeded);
|
||||
|
||||
@ -145,6 +147,9 @@ class Animation : public DOMEventTargetHelper,
|
||||
void UpdatePlaybackRate(double aPlaybackRate);
|
||||
void Reverse(ErrorResult& aRv);
|
||||
|
||||
void Persist();
|
||||
void CommitStyles(ErrorResult& aRv);
|
||||
|
||||
bool IsRunningOnCompositor() const;
|
||||
|
||||
virtual void Tick();
|
||||
@ -327,6 +332,25 @@ class Animation : public DOMEventTargetHelper,
|
||||
bool IsRelevant() const { return mIsRelevant; }
|
||||
void UpdateRelevance();
|
||||
|
||||
// https://drafts.csswg.org/web-animations-1/#replaceable-animation
|
||||
bool IsReplaceable() const;
|
||||
|
||||
/**
|
||||
* Returns true if this Animation satisfies the requirements for being
|
||||
* removed when it is replaced.
|
||||
*
|
||||
* Returning true does not imply this animation _should_ be removed.
|
||||
* Determining that depends on the other effects in the same EffectSet to
|
||||
* which this animation's effect, if any, contributes.
|
||||
*/
|
||||
bool IsRemovable() const;
|
||||
|
||||
/**
|
||||
* Make this animation's target effect no-longer part of the effect stack
|
||||
* while preserving its timing information.
|
||||
*/
|
||||
void Remove();
|
||||
|
||||
/**
|
||||
* Returns true if this Animation has a lower composite order than aOther.
|
||||
*/
|
||||
@ -364,6 +388,8 @@ class Animation : public DOMEventTargetHelper,
|
||||
const nsCSSPropertyIDSet& aPropertiesToSkip);
|
||||
|
||||
void NotifyEffectTimingUpdated();
|
||||
void NotifyEffectPropertiesUpdated();
|
||||
void NotifyEffectTargetUpdated();
|
||||
void NotifyGeometricAnimationsStartingThisFrame();
|
||||
|
||||
/**
|
||||
@ -479,6 +505,9 @@ class Animation : public DOMEventTargetHelper,
|
||||
return GetCurrentTimeForHoldTime(Nullable<TimeDuration>());
|
||||
}
|
||||
|
||||
void ScheduleReplacementCheck();
|
||||
void MaybeScheduleReplacementCheck();
|
||||
|
||||
// Earlier side of the elapsed time range reported in CSS Animations and CSS
|
||||
// Transitions events.
|
||||
//
|
||||
@ -525,7 +554,7 @@ class Animation : public DOMEventTargetHelper,
|
||||
Nullable<TimeDuration> mHoldTime; // Animation timescale
|
||||
Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mPreviousCurrentTime; // Animation timescale
|
||||
double mPlaybackRate;
|
||||
double mPlaybackRate = 1.0;
|
||||
Maybe<double> mPendingPlaybackRate;
|
||||
|
||||
// A Promise that is replaced on each call to Play()
|
||||
@ -552,7 +581,7 @@ class Animation : public DOMEventTargetHelper,
|
||||
|
||||
// While ordering Animation objects for event dispatch, the index of the
|
||||
// target node in its parent may be cached in mCachedChildIndex.
|
||||
int32_t mCachedChildIndex;
|
||||
int32_t mCachedChildIndex = -1;
|
||||
|
||||
// Indicates if the animation is in the pending state (and what state it is
|
||||
// waiting to enter when it finished pending). We use this rather than
|
||||
@ -561,23 +590,28 @@ class Animation : public DOMEventTargetHelper,
|
||||
// from the PendingAnimationTracker while it is waiting for the next tick
|
||||
// (see TriggerOnNextTick for details).
|
||||
enum class PendingState : uint8_t { NotPending, PlayPending, PausePending };
|
||||
PendingState mPendingState;
|
||||
PendingState mPendingState = PendingState::NotPending;
|
||||
|
||||
// Handling of this animation's target effect when filling while finished.
|
||||
AnimationReplaceState mReplaceState = AnimationReplaceState::Active;
|
||||
|
||||
bool mFinishedAtLastComposeStyle = false;
|
||||
bool mWasReplaceableAtLastTick = false;
|
||||
|
||||
bool mFinishedAtLastComposeStyle;
|
||||
// Indicates that the animation should be exposed in an element's
|
||||
// getAnimations() list.
|
||||
bool mIsRelevant;
|
||||
bool mIsRelevant = false;
|
||||
|
||||
// True if mFinished is resolved or would be resolved if mFinished has
|
||||
// yet to be created. This is not set when mFinished is rejected since
|
||||
// in that case mFinished is immediately reset to represent a new current
|
||||
// finished promise.
|
||||
bool mFinishedIsResolved;
|
||||
bool mFinishedIsResolved = false;
|
||||
|
||||
// True if this animation was triggered at the same time as one or more
|
||||
// geometric animations and hence we should run any transform animations on
|
||||
// the main thread.
|
||||
bool mSyncWithGeometricAnimations;
|
||||
bool mSyncWithGeometricAnimations = false;
|
||||
|
||||
RefPtr<MicroTaskRunnable> mFinishNotificationTask;
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/KeyframeEffect.h"
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
@ -77,7 +78,7 @@ void AnimationEffect::SetSpecifiedTiming(TimingParams&& aTiming) {
|
||||
mAnimation->NotifyEffectTimingUpdated();
|
||||
|
||||
if (mAnimation->IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(mAnimation);
|
||||
MutationObservers::NotifyAnimationChanged(mAnimation);
|
||||
}
|
||||
|
||||
if (AsKeyframeEffect()) {
|
||||
@ -180,9 +181,9 @@ ComputedTiming AnimationEffect::GetComputedTimingAt(
|
||||
// Determine the 0-based index of the current iteration.
|
||||
// https://drafts.csswg.org/web-animations/#current-iteration
|
||||
result.mCurrentIteration =
|
||||
(result.mIterations >= UINT64_MAX &&
|
||||
(result.mIterations >= double(UINT64_MAX) &&
|
||||
result.mPhase == ComputedTiming::AnimationPhase::After) ||
|
||||
overallProgress >= UINT64_MAX
|
||||
overallProgress >= double(UINT64_MAX)
|
||||
? UINT64_MAX // In GetComputedTimingDictionary(),
|
||||
// we will convert this into Infinity
|
||||
: static_cast<uint64_t>(overallProgress);
|
||||
|
@ -11,16 +11,15 @@ namespace mozilla {
|
||||
template <uint32_t N>
|
||||
nsresult AnimationPerformanceWarning::ToLocalizedStringWithIntParams(
|
||||
const char* aKey, nsAString& aLocalizedString) const {
|
||||
nsAutoString strings[N];
|
||||
const char16_t* charParams[N];
|
||||
AutoTArray<nsString, N> strings;
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mParams->Length() == N);
|
||||
for (size_t i = 0, n = mParams->Length(); i < n; i++) {
|
||||
strings[i].AppendInt((*mParams)[i]);
|
||||
charParams[i] = strings[i].get();
|
||||
strings.AppendElement()->AppendInt((*mParams)[i]);
|
||||
}
|
||||
|
||||
return nsContentUtils::FormatLocalizedString(
|
||||
nsContentUtils::eLAYOUT_PROPERTIES, aKey, charParams, aLocalizedString);
|
||||
nsContentUtils::eLAYOUT_PROPERTIES, aKey, strings, aLocalizedString);
|
||||
}
|
||||
|
||||
bool AnimationPerformanceWarning::ToLocalizedString(
|
||||
@ -32,13 +31,13 @@ bool AnimationPerformanceWarning::ToLocalizedString(
|
||||
MOZ_ASSERT(mParams && mParams->Length() == 6,
|
||||
"Parameter's length should be 6 for ContentTooLarge2");
|
||||
|
||||
return NS_SUCCEEDED(ToLocalizedStringWithIntParams<7>(
|
||||
return NS_SUCCEEDED(ToLocalizedStringWithIntParams<6>(
|
||||
"CompositorAnimationWarningContentTooLarge2", aLocalizedString));
|
||||
case Type::ContentTooLargeArea:
|
||||
MOZ_ASSERT(mParams && mParams->Length() == 2,
|
||||
"Parameter's length should be 2 for ContentTooLargeArea");
|
||||
|
||||
return NS_SUCCEEDED(ToLocalizedStringWithIntParams<3>(
|
||||
return NS_SUCCEEDED(ToLocalizedStringWithIntParams<2>(
|
||||
"CompositorAnimationWarningContentTooLargeArea", aLocalizedString));
|
||||
case Type::TransformBackfaceVisibilityHidden:
|
||||
key = "CompositorAnimationWarningTransformBackfaceVisibilityHidden";
|
||||
|
@ -6,6 +6,8 @@
|
||||
#define mozilla_AnimationTarget_h
|
||||
|
||||
#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF
|
||||
#include "mozilla/HashFunctions.h" // For HashNumber, AddToHash
|
||||
#include "mozilla/HashTable.h" // For DefaultHasher, PointerHasher
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
@ -67,6 +69,26 @@ inline void ImplCycleCollectionUnlink(Maybe<OwningAnimationTarget>& aTarget) {
|
||||
}
|
||||
}
|
||||
|
||||
// A DefaultHasher specialization for OwningAnimationTarget.
|
||||
template <>
|
||||
struct DefaultHasher<OwningAnimationTarget> {
|
||||
using Key = OwningAnimationTarget;
|
||||
using Lookup = OwningAnimationTarget;
|
||||
using PtrHasher = PointerHasher<dom::Element*>;
|
||||
|
||||
static HashNumber hash(const Lookup& aLookup) {
|
||||
return AddToHash(PtrHasher::hash(aLookup.mElement.get()),
|
||||
static_cast<uint8_t>(aLookup.mPseudoType));
|
||||
}
|
||||
|
||||
static bool match(const Key& aKey, const Lookup& aLookup) {
|
||||
return PtrHasher::match(aKey.mElement.get(), aLookup.mElement.get()) &&
|
||||
aKey.mPseudoType == aLookup.mPseudoType;
|
||||
}
|
||||
|
||||
static void rekey(Key& aKey, Key&& aNewKey) { aKey = std::move(aNewKey); }
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_AnimationTarget_h
|
||||
|
@ -43,7 +43,7 @@ JSObject* CSSPseudoElement::WrapObject(JSContext* aCx,
|
||||
return CSSPseudoElement_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void CSSPseudoElement::GetAnimations(const AnimationFilter& filter,
|
||||
void CSSPseudoElement::GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aRetVal) {
|
||||
Document* doc = mOriginatingElement->GetComposedDoc();
|
||||
if (doc) {
|
||||
|
@ -52,7 +52,7 @@ class CSSPseudoElement final : public nsWrapperCache {
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
void GetAnimations(const AnimationFilter& filter,
|
||||
void GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aRetVal);
|
||||
already_AddRefed<Animation> Animate(
|
||||
JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
|
||||
|
@ -186,13 +186,6 @@ void DocumentTimeline::MostRecentRefreshTimeUpdated() {
|
||||
}
|
||||
|
||||
void DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime) {
|
||||
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events,
|
||||
// step2.
|
||||
// Note that this should be done before nsAutoAnimationMutationBatch which is
|
||||
// inside MostRecentRefreshTimeUpdated(). If PerformMicroTaskCheckpoint was
|
||||
// called before nsAutoAnimationMutationBatch is destroyed, some mutation
|
||||
// records might not be delivered in this checkpoint.
|
||||
nsAutoMicroTask mt;
|
||||
MostRecentRefreshTimeUpdated();
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "mozilla/ServoBindings.h" // Servo_GetProperties_Overriding_Animation
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/StaticPrefs_layers.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/TypeTraits.h" // For std::forward<>
|
||||
#include "nsContentUtils.h"
|
||||
@ -71,7 +72,7 @@ bool EffectCompositor::AllowCompositorAnimationsOnFrame(
|
||||
}
|
||||
|
||||
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
||||
if (StaticPrefs::layers_offmainthreadcomposition_log_animations()) {
|
||||
nsCString message;
|
||||
message.AppendLiteral(
|
||||
"Performance warning: Async animations are "
|
||||
@ -392,6 +393,26 @@ class EffectCompositeOrderComparator {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static void ComposeSortedEffects(
|
||||
const nsTArray<KeyframeEffect*>& aSortedEffects,
|
||||
const EffectSet* aEffectSet, EffectCompositor::CascadeLevel aCascadeLevel,
|
||||
RawServoAnimationValueMap* aAnimationValues) {
|
||||
// If multiple animations affect the same property, animations with higher
|
||||
// composite order (priority) override or add to animations with lower
|
||||
// priority.
|
||||
nsCSSPropertyIDSet propertiesToSkip;
|
||||
if (aEffectSet) {
|
||||
propertiesToSkip =
|
||||
aCascadeLevel == EffectCompositor::CascadeLevel::Animations
|
||||
? aEffectSet->PropertiesForAnimationsLevel().Inverse()
|
||||
: aEffectSet->PropertiesForAnimationsLevel();
|
||||
}
|
||||
|
||||
for (KeyframeEffect* effect : aSortedEffects) {
|
||||
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectCompositor::GetServoAnimationRule(
|
||||
const dom::Element* aElement, PseudoStyleType aPseudoType,
|
||||
CascadeLevel aCascadeLevel, RawServoAnimationValueMap* aAnimationValues) {
|
||||
@ -415,16 +436,8 @@ bool EffectCompositor::GetServoAnimationRule(
|
||||
}
|
||||
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
||||
|
||||
// If multiple animations affect the same property, animations with higher
|
||||
// composite order (priority) override or add or animations with lower
|
||||
// priority.
|
||||
const nsCSSPropertyIDSet propertiesToSkip =
|
||||
aCascadeLevel == CascadeLevel::Animations
|
||||
? effectSet->PropertiesForAnimationsLevel().Inverse()
|
||||
: effectSet->PropertiesForAnimationsLevel();
|
||||
for (KeyframeEffect* effect : sortedEffectList) {
|
||||
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
|
||||
}
|
||||
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
|
||||
aAnimationValues);
|
||||
|
||||
MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType),
|
||||
"EffectSet should not change while composing style");
|
||||
@ -432,6 +445,59 @@ bool EffectCompositor::GetServoAnimationRule(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EffectCompositor::ComposeServoAnimationRuleForEffect(
|
||||
KeyframeEffect& aEffect, CascadeLevel aCascadeLevel,
|
||||
RawServoAnimationValueMap* aAnimationValues) {
|
||||
MOZ_ASSERT(aAnimationValues);
|
||||
MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(),
|
||||
"Should not be in print preview");
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target = aEffect.GetTarget();
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't try to compose animations for elements in documents without a pres
|
||||
// shell (e.g. XMLHttpRequest documents).
|
||||
if (!nsContentUtils::GetPresShellForContent(target->mElement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetServoAnimationRule is called as part of the regular style resolution
|
||||
// where the cascade results are updated in the pre-traversal as needed.
|
||||
// This function, however, is only called when committing styles so we
|
||||
// need to ensure the cascade results are up-to-date manually.
|
||||
EffectCompositor::MaybeUpdateCascadeResults(target->mElement,
|
||||
target->mPseudoType);
|
||||
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(target->mElement, target->mPseudoType);
|
||||
|
||||
// Get a list of effects sorted by composite order up to and including
|
||||
// |aEffect|, even if it is not in the EffectSet.
|
||||
auto comparator = EffectCompositeOrderComparator();
|
||||
nsTArray<KeyframeEffect*> sortedEffectList(effectSet ? effectSet->Count() + 1
|
||||
: 1);
|
||||
if (effectSet) {
|
||||
for (KeyframeEffect* effect : *effectSet) {
|
||||
if (comparator.LessThan(effect, &aEffect)) {
|
||||
sortedEffectList.AppendElement(effect);
|
||||
}
|
||||
}
|
||||
sortedEffectList.Sort(comparator);
|
||||
}
|
||||
sortedEffectList.AppendElement(&aEffect);
|
||||
|
||||
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
|
||||
aAnimationValues);
|
||||
|
||||
MOZ_ASSERT(effectSet ==
|
||||
EffectSet::GetEffectSet(target->mElement, target->mPseudoType),
|
||||
"EffectSet should not change while composing style");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ dom::Element* EffectCompositor::GetElementToRestyle(
|
||||
dom::Element* aElement, PseudoStyleType aPseudoType) {
|
||||
if (aPseudoType == PseudoStyleType::NotPseudo) {
|
||||
@ -860,4 +926,53 @@ bool EffectCompositor::PreTraverseInSubtree(ServoTraversalFlags aFlags,
|
||||
return foundElementsNeedingRestyle;
|
||||
}
|
||||
|
||||
void EffectCompositor::NoteElementForReducing(
|
||||
const NonOwningAnimationTarget& aTarget) {
|
||||
if (!StaticPrefs::dom_animations_api_autoremove_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Unused << mElementsToReduce.put(
|
||||
OwningAnimationTarget{aTarget.mElement, aTarget.mPseudoType});
|
||||
}
|
||||
|
||||
static void ReduceEffectSet(EffectSet& aEffectSet) {
|
||||
// Get a list of effects sorted by composite order.
|
||||
nsTArray<KeyframeEffect*> sortedEffectList(aEffectSet.Count());
|
||||
for (KeyframeEffect* effect : aEffectSet) {
|
||||
sortedEffectList.AppendElement(effect);
|
||||
}
|
||||
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
||||
|
||||
nsCSSPropertyIDSet setProperties;
|
||||
|
||||
// Iterate in reverse
|
||||
for (auto iter = sortedEffectList.rbegin(); iter != sortedEffectList.rend();
|
||||
++iter) {
|
||||
MOZ_ASSERT(*iter && (*iter)->GetAnimation(),
|
||||
"Effect in an EffectSet should have an animation");
|
||||
KeyframeEffect& effect = **iter;
|
||||
Animation& animation = *effect.GetAnimation();
|
||||
if (animation.IsRemovable() &&
|
||||
effect.GetPropertySet().IsSubsetOf(setProperties)) {
|
||||
animation.Remove();
|
||||
} else if (animation.IsReplaceable()) {
|
||||
setProperties |= effect.GetPropertySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EffectCompositor::ReduceAnimations() {
|
||||
for (auto iter = mElementsToReduce.iter(); !iter.done(); iter.next()) {
|
||||
const OwningAnimationTarget& target = iter.get();
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
|
||||
if (effectSet) {
|
||||
ReduceEffectSet(*effectSet);
|
||||
}
|
||||
}
|
||||
|
||||
mElementsToReduce.clear();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -6,7 +6,9 @@
|
||||
#define mozilla_EffectCompositor_h
|
||||
|
||||
#include "mozilla/AnimationPerformanceWarning.h"
|
||||
#include "mozilla/AnimationTarget.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/HashTable.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
#include "mozilla/PseudoElementHashEntry.h"
|
||||
@ -36,6 +38,7 @@ struct NonOwningAnimationTarget;
|
||||
namespace dom {
|
||||
class Animation;
|
||||
class Element;
|
||||
class KeyframeEffect;
|
||||
} // namespace dom
|
||||
|
||||
class EffectCompositor {
|
||||
@ -114,8 +117,9 @@ class EffectCompositor {
|
||||
dom::Element* aElement,
|
||||
PseudoStyleType aPseudoType);
|
||||
|
||||
// Get animation rule for stylo. This is an equivalent of GetAnimationRule
|
||||
// and will be called from servo side.
|
||||
// Get the animation rule for the appropriate level of the cascade for
|
||||
// a (pseudo-)element. Called from the Servo side.
|
||||
//
|
||||
// The animation rule is stored in |RawServoAnimationValueMap|.
|
||||
// We need to be careful while doing any modification because it may cause
|
||||
// some thread-safe issues.
|
||||
@ -124,6 +128,15 @@ class EffectCompositor {
|
||||
CascadeLevel aCascadeLevel,
|
||||
RawServoAnimationValueMap* aAnimationValues);
|
||||
|
||||
// A variant on GetServoAnimationRule that composes all the effects for an
|
||||
// element up to and including |aEffect|.
|
||||
//
|
||||
// Note that |aEffect| might not be in the EffectSet since we can use this for
|
||||
// committing the computed style of a removed Animation.
|
||||
bool ComposeServoAnimationRuleForEffect(
|
||||
dom::KeyframeEffect& aEffect, CascadeLevel aCascadeLevel,
|
||||
RawServoAnimationValueMap* aAnimationValues);
|
||||
|
||||
bool HasPendingStyleUpdates() const;
|
||||
|
||||
static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
@ -197,6 +210,12 @@ class EffectCompositor {
|
||||
// at aElement.
|
||||
bool PreTraverseInSubtree(ServoTraversalFlags aFlags, dom::Element* aRoot);
|
||||
|
||||
// Record a (pseudo-)element that may have animations that can be removed.
|
||||
void NoteElementForReducing(const NonOwningAnimationTarget& aTarget);
|
||||
|
||||
bool NeedsReducing() const { return !mElementsToReduce.empty(); }
|
||||
void ReduceAnimations();
|
||||
|
||||
// Returns the target element for restyling.
|
||||
//
|
||||
// If |aPseudoType| is ::after, ::before or ::marker, returns the generated
|
||||
@ -237,6 +256,8 @@ class EffectCompositor {
|
||||
mElementsToRestyle;
|
||||
|
||||
bool mIsInPreTraverse = false;
|
||||
|
||||
HashSet<OwningAnimationTarget> mElementsToReduce;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -10,6 +10,7 @@
|
||||
// For UnrestrictedDoubleOrKeyframeAnimationOptions;
|
||||
#include "mozilla/dom/CSSPseudoElement.h"
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h"
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/ComputedStyleInlines.h"
|
||||
@ -21,7 +22,9 @@
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/PresShellInlines.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_gfx.h"
|
||||
#include "mozilla/StaticPrefs_layers.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "Layers.h" // For Layer
|
||||
#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetComputedStyle
|
||||
@ -31,7 +34,6 @@
|
||||
#include "nsCSSPseudoElements.h" // For PseudoStyleType
|
||||
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsPresContextInlines.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
@ -46,7 +48,7 @@ void AnimationProperty::SetPerformanceWarning(
|
||||
mPerformanceWarning = Some(aWarning);
|
||||
|
||||
nsAutoString localizedString;
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled() &&
|
||||
if (StaticPrefs::layers_offmainthreadcomposition_log_animations() &&
|
||||
mPerformanceWarning->ToLocalizedString(localizedString)) {
|
||||
nsAutoCString logMessage = NS_ConvertUTF16toUTF8(localizedString);
|
||||
AnimationUtils::LogAsyncAnimationFailure(logMessage, aElement);
|
||||
@ -105,7 +107,7 @@ void KeyframeEffect::SetIterationComposite(
|
||||
}
|
||||
|
||||
if (mAnimation && mAnimation->IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(mAnimation);
|
||||
MutationObservers::NotifyAnimationChanged(mAnimation);
|
||||
}
|
||||
|
||||
mEffectOptions.mIterationComposite = aIterationComposite;
|
||||
@ -124,7 +126,7 @@ void KeyframeEffect::SetComposite(const CompositeOperation& aComposite) {
|
||||
mEffectOptions.mComposite = aComposite;
|
||||
|
||||
if (mAnimation && mAnimation->IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(mAnimation);
|
||||
MutationObservers::NotifyAnimationChanged(mAnimation);
|
||||
}
|
||||
|
||||
if (mTarget) {
|
||||
@ -145,7 +147,7 @@ void KeyframeEffect::NotifySpecifiedTimingUpdated() {
|
||||
mAnimation->NotifyEffectTimingUpdated();
|
||||
|
||||
if (mAnimation->IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(mAnimation);
|
||||
MutationObservers::NotifyAnimationChanged(mAnimation);
|
||||
}
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
@ -238,7 +240,7 @@ void KeyframeEffect::SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
|
||||
KeyframeUtils::DistributeKeyframes(mKeyframes);
|
||||
|
||||
if (mAnimation && mAnimation->IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(mAnimation);
|
||||
MutationObservers::NotifyAnimationChanged(mAnimation);
|
||||
}
|
||||
|
||||
// We need to call UpdateProperties() unless the target element doesn't have
|
||||
@ -315,7 +317,7 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
|
||||
|
||||
nsCSSPropertyIDSet properties;
|
||||
|
||||
if (!IsInEffect() && !IsCurrent()) {
|
||||
if (!mAnimation || !mAnimation->IsRelevant()) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@ -340,14 +342,14 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool KeyframeEffect::HasAnimationOfPropertySet(
|
||||
const nsCSSPropertyIDSet& aPropertySet) const {
|
||||
nsCSSPropertyIDSet KeyframeEffect::GetPropertySet() const {
|
||||
nsCSSPropertyIDSet result;
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
if (aPropertySet.HasProperty(property.mProperty)) {
|
||||
return true;
|
||||
result.AddProperty(property.mProperty);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -416,6 +418,10 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
|
||||
|
||||
MarkCascadeNeedsUpdate();
|
||||
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectPropertiesUpdated();
|
||||
}
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
}
|
||||
|
||||
@ -573,13 +579,10 @@ void KeyframeEffect::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
|
||||
if (HasPropertiesThatMightAffectOverflow()) {
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(mTarget->mElement);
|
||||
if (presContext) {
|
||||
TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
MOZ_ASSERT(effectSet,
|
||||
"ComposeStyle should only be called on an effect "
|
||||
"that is part of an effect set");
|
||||
if (presContext && effectSet) {
|
||||
TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
|
||||
effectSet->UpdateLastOverflowAnimationSyncTime(now);
|
||||
}
|
||||
}
|
||||
@ -790,7 +793,9 @@ void KeyframeEffect::UpdateTargetRegistration() {
|
||||
// something calls Animation::UpdateRelevance. Whenever our timing changes,
|
||||
// we should be notifying our Animation before calling this, so
|
||||
// Animation::IsRelevant() should be up-to-date by the time we get here.
|
||||
MOZ_ASSERT(isRelevant == IsCurrent() || IsInEffect(),
|
||||
MOZ_ASSERT(isRelevant ==
|
||||
((IsCurrent() || IsInEffect()) && mAnimation &&
|
||||
mAnimation->ReplaceState() != AnimationReplaceState::Removed),
|
||||
"Out of date Animation::IsRelevant value");
|
||||
|
||||
if (isRelevant && !mInEffectSet) {
|
||||
@ -976,7 +981,7 @@ void KeyframeEffect::SetTarget(
|
||||
|
||||
nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc());
|
||||
if (mAnimation) {
|
||||
nsNodeUtils::AnimationRemoved(mAnimation);
|
||||
MutationObservers::NotifyAnimationRemoved(mAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -993,10 +998,14 @@ void KeyframeEffect::SetTarget(
|
||||
|
||||
nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc());
|
||||
if (mAnimation) {
|
||||
nsNodeUtils::AnimationAdded(mAnimation);
|
||||
MutationObservers::NotifyAnimationAdded(mAnimation);
|
||||
mAnimation->ReschedulePendingTasks();
|
||||
}
|
||||
}
|
||||
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectTargetUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
static void CreatePropertyValue(
|
||||
@ -1726,6 +1735,11 @@ bool KeyframeEffect::ContainsAnimatedScale(const nsIFrame* aFrame) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mAnimation ||
|
||||
mAnimation->ReplaceState() == AnimationReplaceState::Removed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const AnimationProperty& prop : mProperties) {
|
||||
if (prop.mProperty != eCSSProperty_transform &&
|
||||
prop.mProperty != eCSSProperty_scale &&
|
||||
|
@ -72,7 +72,7 @@ struct AnimationProperty {
|
||||
|
||||
Maybe<AnimationPerformanceWarning> mPerformanceWarning;
|
||||
|
||||
InfallibleTArray<AnimationPropertySegment> mSegments;
|
||||
nsTArray<AnimationPropertySegment> mSegments;
|
||||
|
||||
// The copy constructor/assignment doesn't copy mIsRunningOnCompositor and
|
||||
// mPerformanceWarning.
|
||||
@ -99,7 +99,7 @@ struct AnimationProperty {
|
||||
}
|
||||
|
||||
void SetPerformanceWarning(const AnimationPerformanceWarning& aWarning,
|
||||
const Element* aElement);
|
||||
const dom::Element* aElement);
|
||||
};
|
||||
|
||||
struct ElementPropertyTransition;
|
||||
@ -181,9 +181,15 @@ class KeyframeEffect : public AnimationEffect {
|
||||
void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
|
||||
const ComputedStyle* aStyle);
|
||||
|
||||
// Returns the set of properties affected by this effect regardless of
|
||||
// whether any of these properties is overridden by an !important rule.
|
||||
nsCSSPropertyIDSet GetPropertySet() const;
|
||||
|
||||
// Returns true if the effect includes a property in |aPropertySet| regardless
|
||||
// of whether any property in the set is overridden by !important rule.
|
||||
bool HasAnimationOfPropertySet(const nsCSSPropertyIDSet& aPropertySet) const;
|
||||
// of whether any property in the set is overridden by an !important rule.
|
||||
bool HasAnimationOfPropertySet(const nsCSSPropertyIDSet& aPropertySet) const {
|
||||
return GetPropertySet().Intersects(aPropertySet);
|
||||
}
|
||||
|
||||
// GetEffectiveAnimationOfProperty returns AnimationProperty corresponding
|
||||
// to a given CSS property if the effect includes the property and the
|
||||
@ -237,9 +243,7 @@ class KeyframeEffect : public AnimationEffect {
|
||||
nsCSSPropertyIDSet GetPropertiesForCompositor(EffectSet& aEffects,
|
||||
const nsIFrame* aFrame) const;
|
||||
|
||||
const InfallibleTArray<AnimationProperty>& Properties() const {
|
||||
return mProperties;
|
||||
}
|
||||
const nsTArray<AnimationProperty>& Properties() const { return mProperties; }
|
||||
|
||||
// Update |mProperties| by recalculating from |mKeyframes| using
|
||||
// |aComputedStyle| to resolve specified values.
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "mozilla/ServoBindingTypes.h"
|
||||
#include "mozilla/ServoCSSParser.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/TimingParams.h"
|
||||
#include "mozilla/dom/BaseKeyframeTypesBinding.h" // For FastBaseKeyframe etc.
|
||||
#include "mozilla/dom/Document.h" // For Document::AreWebAnimationsImplicitKeyframesEnabled
|
||||
@ -365,8 +365,8 @@ static void GetKeyframeListFromKeyframeSequence(JSContext* aCx,
|
||||
// Check that the keyframes are loosely sorted and with values all
|
||||
// between 0% and 100%.
|
||||
if (!HasValidOffsets(aResult)) {
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_KEYFRAME_OFFSETS>();
|
||||
aResult.Clear();
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_KEYFRAME_OFFSETS>();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -500,7 +500,7 @@ static bool GetPropertyValuesPairs(JSContext* aCx,
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0, n = ids.length(); i < n; i++) {
|
||||
nsAutoJSString propName;
|
||||
nsAutoJSCString propName;
|
||||
if (!propName.init(aCx, ids[i])) {
|
||||
return false;
|
||||
}
|
||||
@ -591,14 +591,13 @@ static bool AppendValueAsString(JSContext* aCx, nsTArray<nsString>& aValues,
|
||||
static void ReportInvalidPropertyValueToConsole(
|
||||
nsCSSPropertyID aProperty, const nsAString& aInvalidPropertyValue,
|
||||
dom::Document* aDoc) {
|
||||
const nsString& invalidValue = PromiseFlatString(aInvalidPropertyValue);
|
||||
const NS_ConvertASCIItoUTF16 propertyName(
|
||||
nsCSSProps::GetStringValue(aProperty));
|
||||
const char16_t* params[] = {invalidValue.get(), propertyName.get()};
|
||||
AutoTArray<nsString, 2> params;
|
||||
params.AppendElement(aInvalidPropertyValue);
|
||||
CopyASCIItoUTF16(nsCSSProps::GetStringValue(aProperty),
|
||||
*params.AppendElement());
|
||||
nsContentUtils::ReportToConsole(
|
||||
nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Animation"), aDoc,
|
||||
nsContentUtils::eDOM_PROPERTIES, "InvalidKeyframePropertyValue", params,
|
||||
ArrayLength(params));
|
||||
nsContentUtils::eDOM_PROPERTIES, "InvalidKeyframePropertyValue", params);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1057,8 +1056,8 @@ static void GetKeyframeListFromPropertyIndexedKeyframe(
|
||||
// offsets are thrown before exceptions arising from invalid easings, we check
|
||||
// the offsets here.
|
||||
if (!HasValidOffsets(aResult)) {
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_KEYFRAME_OFFSETS>();
|
||||
aResult.Clear();
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_KEYFRAME_OFFSETS>();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
dom.animations-api.autoremove.enabled=true
|
||||
dom.animations-api.compositing.enabled=true
|
||||
gfx.omta.background-color=true
|
||||
layout.css.individual-transform.enabled=true
|
||||
|
@ -40,6 +40,7 @@ var gObserver = new MutationObserver(newRecords => {
|
||||
});
|
||||
|
||||
function setupAsynchronousObserver(t, options) {
|
||||
|
||||
gRecords = [];
|
||||
t.add_cleanup(() => {
|
||||
gObserver.disconnect();
|
||||
@ -578,5 +579,86 @@ promise_test(t => {
|
||||
});
|
||||
}, "tree_ordering: subtree");
|
||||
|
||||
runTest();
|
||||
// Test that animations removed by auto-removal trigger an event
|
||||
promise_test(async t => {
|
||||
setupAsynchronousObserver(t, { observe: div, subtree: false });
|
||||
|
||||
// Start two animations such that one will be auto-removed
|
||||
const animA = div.animate(
|
||||
{ opacity: 1 },
|
||||
{ duration: 100 * MS_PER_SEC, fill: 'forwards' }
|
||||
);
|
||||
const animB = div.animate(
|
||||
{ opacity: 1 },
|
||||
{ duration: 100 * MS_PER_SEC, fill: 'forwards' }
|
||||
);
|
||||
|
||||
// Wait for the MutationRecords corresponding to each addition.
|
||||
await waitForNextFrame();
|
||||
|
||||
assert_records(
|
||||
[
|
||||
{ added: [animA], changed: [], removed: [] },
|
||||
{ added: [animB], changed: [], removed: [] },
|
||||
],
|
||||
'records after animation start'
|
||||
);
|
||||
|
||||
// Finish the animations -- this should cause animA to be replaced, and
|
||||
// automatically removed.
|
||||
animA.finish();
|
||||
animB.finish();
|
||||
|
||||
// Wait for the MutationRecords corresponding to the timing changes and the
|
||||
// subsequent removal to be delivered.
|
||||
await waitForNextFrame();
|
||||
|
||||
assert_records(
|
||||
[
|
||||
{ added: [], changed: [animA], removed: [] },
|
||||
{ added: [], changed: [animB], removed: [] },
|
||||
{ added: [], changed: [], removed: [animA] },
|
||||
],
|
||||
'records after finishing'
|
||||
);
|
||||
|
||||
// Restore animA.
|
||||
animA.persist();
|
||||
|
||||
// Wait for the MutationRecord corresponding to the re-addition of animA.
|
||||
await waitForNextFrame();
|
||||
|
||||
assert_records(
|
||||
[{ added: [animA], changed: [], removed: [] }],
|
||||
'records after persisting'
|
||||
);
|
||||
|
||||
// Tidy up
|
||||
animA.cancel();
|
||||
animB.cancel();
|
||||
|
||||
await waitForNextFrame();
|
||||
|
||||
assert_records(
|
||||
[
|
||||
{ added: [], changed: [], removed: [animA] },
|
||||
{ added: [], changed: [], removed: [animB] },
|
||||
],
|
||||
'records after tidying up end'
|
||||
);
|
||||
}, 'Animations automatically removed are reported');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{
|
||||
set: [
|
||||
["dom.animations-api.autoremove.enabled", true],
|
||||
["dom.animations-api.implicit-keyframes.enabled", true],
|
||||
],
|
||||
},
|
||||
function() {
|
||||
runTest();
|
||||
done();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
@ -43,7 +43,7 @@ function setupSynchronousObserver(t, target, subtree) {
|
||||
t.add_cleanup(() => {
|
||||
observer.disconnect();
|
||||
});
|
||||
observer.observe(target, { animations: true, subtree: subtree });
|
||||
observer.observe(target, { animations: true, subtree });
|
||||
return observer;
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ function assert_properties_equal(actual, expected) {
|
||||
|
||||
// Shorthand for constructing a value object
|
||||
function value(offset, value, composite, easing) {
|
||||
return { offset: offset, value: value, easing: easing, composite: composite };
|
||||
return { offset, value, easing, composite };
|
||||
}
|
||||
|
||||
var gTests = [
|
||||
|
@ -14,6 +14,7 @@ support-files =
|
||||
chrome/file_animate_xrays.html
|
||||
mozilla/xhr_doc.html
|
||||
mozilla/file_deferred_start.html
|
||||
mozilla/file_disable_animations_api_autoremove.html
|
||||
mozilla/file_disable_animations_api_compositing.html
|
||||
mozilla/file_disable_animations_api_get_animations.html
|
||||
mozilla/file_disable_animations_api_implicit_keyframes.html
|
||||
@ -32,6 +33,7 @@ skip-if = (verify && !debug && (os == 'mac'))
|
||||
[mozilla/test_cubic_bezier_limits.html]
|
||||
[mozilla/test_deferred_start.html]
|
||||
skip-if = (toolkit == 'android' && debug) || (os == 'win' && bits == 64) # Bug 1363957
|
||||
[mozilla/test_disable_animations_api_autoremove.html]
|
||||
[mozilla/test_disable_animations_api_compositing.html]
|
||||
[mozilla/test_disable_animations_api_get_animations.html]
|
||||
[mozilla/test_disable_animations_api_implicit_keyframes.html]
|
||||
|
@ -0,0 +1,69 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
|
||||
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
|
||||
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
|
||||
|
||||
// This should be assert_not_own_property but our local copy of testharness.js
|
||||
// is old.
|
||||
assert_equals(
|
||||
animA.replaceState,
|
||||
undefined,
|
||||
'Should not have a replaceState member'
|
||||
);
|
||||
|
||||
animA.addEventListener(
|
||||
'remove',
|
||||
t.step_func(() => {
|
||||
assert_unreached('Should not fire a remove event');
|
||||
})
|
||||
);
|
||||
|
||||
// Allow a chance for the remove event to be fired
|
||||
|
||||
await animA.finished;
|
||||
await waitForNextFrame();
|
||||
}, 'Remove events should not be fired if the pref is not set');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.opacity = '0.1';
|
||||
|
||||
const animA = div.animate(
|
||||
{ opacity: 0.2 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
const animB = div.animate(
|
||||
{ opacity: 0.3, composite: 'add' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
await animA.finished;
|
||||
|
||||
assert_approx_equals(
|
||||
parseFloat(getComputedStyle(div).opacity),
|
||||
0.5,
|
||||
0.0001,
|
||||
'Covered animation should still contribute to effect stack when adding'
|
||||
);
|
||||
|
||||
animB.cancel();
|
||||
|
||||
assert_approx_equals(
|
||||
parseFloat(getComputedStyle(div).opacity),
|
||||
0.2,
|
||||
0.0001,
|
||||
'Covered animation should still contribute to animated style when replacing'
|
||||
);
|
||||
}, 'Covered animations should still affect style if the pref is not set');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
setup({ explicit_done: true });
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ set: [['dom.animations-api.autoremove.enabled', false]] },
|
||||
function() {
|
||||
window.open('file_disable_animations_api_autoremove.html');
|
||||
}
|
||||
);
|
||||
</script>
|
@ -13,20 +13,20 @@ function waitForSetPref(pref, value) {
|
||||
}
|
||||
|
||||
/*
|
||||
* These tests rely on the fact that the -webkit-text-fill-color property
|
||||
* is disabled by the layout.css.prefixes.webkit pref. If we ever remove that
|
||||
* pref we will need to substitute some other pref:property combination.
|
||||
* These tests rely on the fact that the -webkit-line-clamp property is
|
||||
* disabled by the layout.css.webkit-line-clamp.enabled pref. If we ever remove
|
||||
* that pref we will need to substitute some other pref:property combination.
|
||||
*/
|
||||
|
||||
promise_test(function(t) {
|
||||
return waitForSetPref('layout.css.prefixes.webkit', true).then(() => {
|
||||
var anim = addDiv(t).animate({ webkitTextFillColor: [ 'green', 'blue' ]});
|
||||
return waitForSetPref('layout.css.webkit-line-clamp.enabled', true).then(() => {
|
||||
var anim = addDiv(t).animate({ webkitLineClamp: [ '2', '4' ]});
|
||||
assert_equals(anim.effect.getKeyframes().length, 2,
|
||||
'A property-indexed keyframe specifying only enabled'
|
||||
+ ' properties produces keyframes');
|
||||
return waitForSetPref('layout.css.prefixes.webkit', false);
|
||||
return waitForSetPref('layout.css.webkit-line-clamp.enabled', false);
|
||||
}).then(() => {
|
||||
var anim = addDiv(t).animate({ webkitTextFillColor: [ 'green', 'blue' ]});
|
||||
var anim = addDiv(t).animate({ webkitLineClamp: [ '2', '4' ]});
|
||||
assert_equals(anim.effect.getKeyframes().length, 0,
|
||||
'A property-indexed keyframe specifying only disabled'
|
||||
+ ' properties produces no keyframes');
|
||||
@ -35,8 +35,8 @@ promise_test(function(t) {
|
||||
|
||||
promise_test(function(t) {
|
||||
var createAnim = () => {
|
||||
var anim = addDiv(t).animate([ { webkitTextFillColor: 'green' },
|
||||
{ webkitTextFillColor: 'blue' } ]);
|
||||
var anim = addDiv(t).animate([ { webkitLineClamp: '2' },
|
||||
{ webkitLineClamp: '4' } ]);
|
||||
assert_equals(anim.effect.getKeyframes().length, 2,
|
||||
'Animation specified using a keyframe sequence should'
|
||||
+ ' return the same number of keyframes regardless of'
|
||||
@ -55,17 +55,17 @@ promise_test(function(t) {
|
||||
`${descr} should NOT have the '${property}' property`);
|
||||
};
|
||||
|
||||
return waitForSetPref('layout.css.prefixes.webkit', true).then(() => {
|
||||
return waitForSetPref('layout.css.webkit-line-clamp.enabled', true).then(() => {
|
||||
var anim = createAnim();
|
||||
assert_has_property(anim, 0, 'Initial keyframe', 'webkitTextFillColor');
|
||||
assert_has_property(anim, 1, 'Final keyframe', 'webkitTextFillColor');
|
||||
return waitForSetPref('layout.css.prefixes.webkit', false);
|
||||
assert_has_property(anim, 0, 'Initial keyframe', 'webkitLineClamp');
|
||||
assert_has_property(anim, 1, 'Final keyframe', 'webkitLineClamp');
|
||||
return waitForSetPref('layout.css.webkit-line-clamp.enabled', false);
|
||||
}).then(() => {
|
||||
var anim = createAnim();
|
||||
assert_does_not_have_property(anim, 0, 'Initial keyframe',
|
||||
'webkitTextFillColor');
|
||||
'webkitLineClamp');
|
||||
assert_does_not_have_property(anim, 1, 'Final keyframe',
|
||||
'webkitTextFillColor');
|
||||
'webkitLineClamp');
|
||||
});
|
||||
}, 'Specifying a disabled property using a keyframe sequence');
|
||||
|
||||
|
@ -9,7 +9,6 @@ setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [
|
||||
["layout.css.osx-font-smoothing.enabled", true],
|
||||
["layout.css.prefixes.webkit", true]
|
||||
] },
|
||||
function() {
|
||||
window.open("file_discrete_animations.html");
|
||||
|
@ -60,7 +60,7 @@ const testcases = [
|
||||
property: "-moz-user-modify"
|
||||
},
|
||||
{
|
||||
property: "-moz-user-select"
|
||||
property: "user-select"
|
||||
},
|
||||
{
|
||||
property: "-moz-window-dragging"
|
||||
|
@ -29,7 +29,7 @@ test(function(t) {
|
||||
sandbox.importFunction(document, "document");
|
||||
sandbox.importFunction(assert_true, "assert_true");
|
||||
sandbox.importFunction(assert_unreached, "assert_unreached");
|
||||
SpecialPowers.Cu.evalInSandbox(`(${contentScript.toSource()})()`, sandbox);
|
||||
SpecialPowers.Cu.evalInSandbox(`(${contentScript.toString()})()`, sandbox);
|
||||
}, 'Setting easing should not throw any exceptions in sandbox');
|
||||
|
||||
</script>
|
||||
|
@ -136,7 +136,7 @@ function assert_properties_equal(actual, expected) {
|
||||
* e.g. { offset: 0.1, value: '1px', composite: 'replace', easing: 'ease'}
|
||||
*/
|
||||
function valueFormat(offset, value, composite, easing) {
|
||||
return { offset: offset, value: value, easing: easing, composite: composite };
|
||||
return { offset, value, easing, composite };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,9 +7,7 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -64,7 +62,7 @@ AudioChannelAgent::InitWithWeakCallback(
|
||||
}
|
||||
|
||||
nsresult AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner* aWindow) {
|
||||
mWindow = aWindow->GetScriptableTop();
|
||||
mWindow = aWindow->GetInProcessScriptableTop();
|
||||
if (NS_WARN_IF(!mWindow)) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -76,7 +74,7 @@ nsresult AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner* aWindow) {
|
||||
// iframe (what is controlled by the system app).
|
||||
// For doing this we go recursively back into the chain of windows until we
|
||||
// find apps that are not the system one.
|
||||
nsCOMPtr<nsPIDOMWindowOuter> outerParent = mWindow->GetParent();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> outerParent = mWindow->GetInProcessParent();
|
||||
if (!outerParent || outerParent == mWindow) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -126,13 +124,25 @@ nsresult AudioChannelAgent::InitInternal(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig,
|
||||
uint8_t aAudible) {
|
||||
if (NS_WARN_IF(!aConfig)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
void AudioChannelAgent::PullInitialUpdate() {
|
||||
RefPtr<AudioChannelService> service = AudioChannelService::Get();
|
||||
MOZ_ASSERT(service);
|
||||
MOZ_ASSERT(mIsRegToService);
|
||||
|
||||
AudioPlaybackConfig config = service->GetMediaConfig(mWindow);
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelAgent, PullInitialUpdate, this=%p, "
|
||||
"mute=%s, volume=%f, suspend=%s, audioCapturing=%s\n",
|
||||
this, config.mMuted ? "true" : "false", config.mVolume,
|
||||
SuspendTypeToStr(config.mSuspend),
|
||||
config.mCapturedAudio ? "true" : "false"));
|
||||
WindowVolumeChanged(config.mVolume, config.mMuted);
|
||||
WindowSuspendChanged(config.mSuspend);
|
||||
WindowAudioCaptureChanged(InnerWindowID(), config.mCapturedAudio);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AudioChannelAgent::NotifyStartedPlaying(uint8_t aAudible) {
|
||||
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
|
||||
if (service == nullptr || mIsRegToService) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -144,18 +154,12 @@ AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig,
|
||||
service->RegisterAudioChannelAgent(
|
||||
this, static_cast<AudioChannelService::AudibleState>(aAudible));
|
||||
|
||||
AudioPlaybackConfig config = service->GetMediaConfig(mWindow);
|
||||
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
|
||||
"audible = %s, mute = %s, volume = %f, suspend = %s\n",
|
||||
("AudioChannelAgent, NotifyStartedPlaying, this = %p, audible = %s\n",
|
||||
this,
|
||||
AudibleStateToStr(
|
||||
static_cast<AudioChannelService::AudibleState>(aAudible)),
|
||||
config.mMuted ? "true" : "false", config.mVolume,
|
||||
SuspendTypeToStr(config.mSuspend)));
|
||||
static_cast<AudioChannelService::AudibleState>(aAudible))));
|
||||
|
||||
aConfig->SetConfig(config.mVolume, config.mMuted, config.mSuspend);
|
||||
mIsRegToService = true;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -210,19 +214,17 @@ AudioChannelAgent::GetCallback() {
|
||||
return callback.forget();
|
||||
}
|
||||
|
||||
void AudioChannelAgent::WindowVolumeChanged() {
|
||||
void AudioChannelAgent::WindowVolumeChanged(float aVolume, bool aMuted) {
|
||||
nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
AudioPlaybackConfig config = GetMediaConfig();
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %s, "
|
||||
"volume = %f\n",
|
||||
this, config.mMuted ? "true" : "false", config.mVolume));
|
||||
|
||||
callback->WindowVolumeChanged(config.mVolume, config.mMuted);
|
||||
this, aMuted ? "true" : "false", aVolume));
|
||||
callback->WindowVolumeChanged(aVolume, aMuted);
|
||||
}
|
||||
|
||||
void AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend) {
|
||||
@ -243,7 +245,7 @@ void AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend) {
|
||||
callback->WindowSuspendChanged(aSuspend);
|
||||
}
|
||||
|
||||
AudioPlaybackConfig AudioChannelAgent::GetMediaConfig() {
|
||||
AudioPlaybackConfig AudioChannelAgent::GetMediaConfig() const {
|
||||
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
|
||||
AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
|
||||
if (service) {
|
||||
@ -282,6 +284,10 @@ void AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID,
|
||||
callback->WindowAudioCaptureChanged(aCapture);
|
||||
}
|
||||
|
||||
bool AudioChannelAgent::IsWindowAudioCapturingEnabled() const {
|
||||
return GetMediaConfig().mCapturedAudio;
|
||||
}
|
||||
|
||||
bool AudioChannelAgent::IsPlayingStarted() const { return mIsRegToService; }
|
||||
|
||||
bool AudioChannelAgent::ShouldBlockMedia() const {
|
||||
|
@ -28,22 +28,30 @@ class AudioChannelAgent : public nsIAudioChannelAgent {
|
||||
|
||||
AudioChannelAgent();
|
||||
|
||||
void WindowVolumeChanged();
|
||||
void WindowSuspendChanged(nsSuspendedTypes aSuspend);
|
||||
void WindowAudioCaptureChanged(uint64_t aInnerWindowID, bool aCapture);
|
||||
|
||||
nsPIDOMWindowOuter* Window() const { return mWindow; }
|
||||
// nsIAudioChannelAgentCallback MUST call this function after calling
|
||||
// NotifyStartedPlaying() to require the initial update for
|
||||
// volume/suspend/audio-capturing which might set before starting the agent.
|
||||
// Ex. starting the agent in a tab which has been muted before, so the agent
|
||||
// should apply mute state to its callback.
|
||||
void PullInitialUpdate();
|
||||
|
||||
uint64_t WindowID() const;
|
||||
uint64_t InnerWindowID() const;
|
||||
|
||||
bool IsWindowAudioCapturingEnabled() const;
|
||||
bool IsPlayingStarted() const;
|
||||
bool ShouldBlockMedia() const;
|
||||
|
||||
private:
|
||||
virtual ~AudioChannelAgent();
|
||||
|
||||
AudioPlaybackConfig GetMediaConfig();
|
||||
friend class AudioChannelService;
|
||||
void WindowVolumeChanged(float aVolume, bool aMuted);
|
||||
void WindowSuspendChanged(nsSuspendedTypes aSuspend);
|
||||
void WindowAudioCaptureChanged(uint64_t aInnerWindowID, bool aCapture);
|
||||
|
||||
nsPIDOMWindowOuter* Window() const { return mWindow; }
|
||||
uint64_t InnerWindowID() const;
|
||||
AudioPlaybackConfig GetMediaConfig() const;
|
||||
bool IsDisposableSuspend(nsSuspendedTypes aSuspend) const;
|
||||
|
||||
// Returns mCallback if that's non-null, or otherwise tries to get an
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
@ -319,7 +318,9 @@ AudioPlaybackConfig AudioChannelService::GetMediaConfig(
|
||||
AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
|
||||
|
||||
if (!aWindow) {
|
||||
config.SetConfig(0.0, true, nsISuspendedTypes::SUSPENDED_BLOCK);
|
||||
config.mVolume = 0.0;
|
||||
config.mMuted = true;
|
||||
config.mSuspend = nsISuspendedTypes::SUSPENDED_BLOCK;
|
||||
return config;
|
||||
}
|
||||
|
||||
@ -336,6 +337,7 @@ AudioPlaybackConfig AudioChannelService::GetMediaConfig(
|
||||
config.mSuspend = winData->mOwningAudioFocus
|
||||
? config.mSuspend
|
||||
: nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE;
|
||||
config.mCapturedAudio = winData->mIsAudioCaptured;
|
||||
}
|
||||
|
||||
config.mVolume *= window->GetAudioVolume();
|
||||
@ -344,7 +346,8 @@ AudioPlaybackConfig AudioChannelService::GetMediaConfig(
|
||||
config.mSuspend = window->GetMediaSuspend();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> win =
|
||||
window->GetInProcessScriptableParentOrNull();
|
||||
if (!win) {
|
||||
break;
|
||||
}
|
||||
@ -404,7 +407,8 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
nsTObserverArray<AudioChannelAgent*>::ForwardIterator iter(
|
||||
winData->mAgents);
|
||||
while (iter.HasMore()) {
|
||||
iter.GetNext()->WindowVolumeChanged();
|
||||
iter.GetNext()->WindowVolumeChanged(winData->mConfig.mVolume,
|
||||
winData->mConfig.mMuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -417,7 +421,7 @@ void AudioChannelService::RefreshAgents(
|
||||
const std::function<void(AudioChannelAgent*)>& aFunc) {
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetInProcessScriptableTop();
|
||||
if (!topWindow) {
|
||||
return;
|
||||
}
|
||||
@ -433,9 +437,11 @@ void AudioChannelService::RefreshAgents(
|
||||
}
|
||||
}
|
||||
|
||||
void AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow) {
|
||||
RefreshAgents(aWindow,
|
||||
[](AudioChannelAgent* agent) { agent->WindowVolumeChanged(); });
|
||||
void AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow,
|
||||
float aVolume, bool aMuted) {
|
||||
RefreshAgents(aWindow, [aVolume, aMuted](AudioChannelAgent* agent) {
|
||||
agent->WindowVolumeChanged(aVolume, aMuted);
|
||||
});
|
||||
}
|
||||
|
||||
void AudioChannelService::RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
|
||||
@ -456,7 +462,7 @@ void AudioChannelService::SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow,
|
||||
"aCapture = %d\n",
|
||||
aWindow, aCapture));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetInProcessScriptableTop();
|
||||
if (!topWindow) {
|
||||
return;
|
||||
}
|
||||
@ -513,7 +519,7 @@ AudioChannelService::AudioChannelWindow* AudioChannelService::GetWindowData(
|
||||
bool AudioChannelService::IsWindowActive(nsPIDOMWindowOuter* aWindow) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
auto* window = nsPIDOMWindowOuter::From(aWindow)->GetScriptableTop();
|
||||
auto* window = nsPIDOMWindowOuter::From(aWindow)->GetInProcessScriptableTop();
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
@ -544,7 +550,7 @@ void AudioChannelService::NotifyMediaResumedFromBlock(
|
||||
nsPIDOMWindowOuter* aWindow) {
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetInProcessScriptableTop();
|
||||
if (!topWindow) {
|
||||
return;
|
||||
}
|
||||
@ -713,7 +719,6 @@ void AudioChannelService::AudioChannelWindow::AppendAgent(
|
||||
|
||||
RequestAudioFocus(aAgent);
|
||||
AppendAgentAndIncreaseAgentsNum(aAgent);
|
||||
AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing);
|
||||
if (aAudible == AudibleState::eAudible) {
|
||||
AudioAudibleChanged(aAgent, AudibleState::eAudible,
|
||||
AudibleChangedReasons::eDataAudibleChanged);
|
||||
@ -728,7 +733,6 @@ void AudioChannelService::AudioChannelWindow::RemoveAgent(
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
RemoveAgentAndReduceAgentsNum(aAgent);
|
||||
AudioCapturedChanged(aAgent, AudioCaptureState::eNotCapturing);
|
||||
AudioAudibleChanged(aAgent, AudibleState::eNotAudible,
|
||||
AudibleChangedReasons::ePauseStateChanged);
|
||||
}
|
||||
@ -784,15 +788,6 @@ void AudioChannelService::AudioChannelWindow::RemoveAgentAndReduceAgentsNum(
|
||||
}
|
||||
}
|
||||
|
||||
void AudioChannelService::AudioChannelWindow::AudioCapturedChanged(
|
||||
AudioChannelAgent* aAgent, AudioCaptureState aCapture) {
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
if (mIsAudioCaptured) {
|
||||
aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(), aCapture);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioChannelService::AudioChannelWindow::AudioAudibleChanged(
|
||||
AudioChannelAgent* aAgent, AudibleState aAudible,
|
||||
AudibleChangedReasons aReason) {
|
||||
|
@ -36,15 +36,10 @@ class AudioPlaybackConfig {
|
||||
mSuspend(aSuspended),
|
||||
mNumberOfAgents(0) {}
|
||||
|
||||
void SetConfig(float aVolume, bool aMuted, uint32_t aSuspended) {
|
||||
mVolume = aVolume;
|
||||
mMuted = aMuted;
|
||||
mSuspend = aSuspended;
|
||||
}
|
||||
|
||||
float mVolume;
|
||||
bool mMuted;
|
||||
uint32_t mSuspend;
|
||||
bool mCapturedAudio = false;
|
||||
uint32_t mNumberOfAgents;
|
||||
};
|
||||
|
||||
@ -118,7 +113,8 @@ class AudioChannelService final : public nsIObserver {
|
||||
|
||||
bool IsWindowActive(nsPIDOMWindowOuter* aWindow);
|
||||
|
||||
void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow);
|
||||
void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow, float aVolume,
|
||||
bool aMuted);
|
||||
void RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
|
||||
nsSuspendedTypes aSuspend);
|
||||
|
||||
@ -182,9 +178,6 @@ class AudioChannelService final : public nsIObserver {
|
||||
bool mShouldSendActiveMediaBlockStopEvent;
|
||||
|
||||
private:
|
||||
void AudioCapturedChanged(AudioChannelAgent* aAgent,
|
||||
AudioCaptureState aCapture);
|
||||
|
||||
void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
|
||||
AudibleChangedReasons aReason);
|
||||
void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent,
|
||||
|
@ -55,16 +55,6 @@ interface nsISuspendedTypes : nsISupports
|
||||
const uint32_t SUSPENDED_STOP_DISPOSABLE = 4;
|
||||
};
|
||||
|
||||
%{C++
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
// It's defined in dom/audiochannel/AudioChannelService.h.
|
||||
class AudioPlaybackConfig;
|
||||
}
|
||||
}
|
||||
%}
|
||||
[ptr] native AudioPlaybackConfig(mozilla::dom::AudioPlaybackConfig);
|
||||
|
||||
[uuid(15c05894-408e-4798-b527-a8c32d9c5f8c)]
|
||||
interface nsIAudioChannelAgentCallback : nsISupports
|
||||
{
|
||||
@ -131,11 +121,8 @@ interface nsIAudioChannelAgent : nsISupports
|
||||
* Notify the agent that we want to start playing.
|
||||
* Note: Gecko component SHOULD call this function first then start to
|
||||
* play audio stream only when return value is true.
|
||||
*
|
||||
* @param config
|
||||
* It contains the playback related states (volume/mute/suspend)
|
||||
*/
|
||||
void notifyStartedPlaying(in AudioPlaybackConfig config, in uint8_t audible);
|
||||
void notifyStartedPlaying(in uint8_t audible);
|
||||
|
||||
/**
|
||||
* Notify the agent we no longer want to play.
|
||||
|
148
dom/base/AbstractRange.cpp
Normal file
148
dom/base/AbstractRange.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/* 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 "mozilla/dom/AbstractRange.h"
|
||||
#include "mozilla/dom/AbstractRangeBinding.h"
|
||||
|
||||
#include "mozilla/RangeUtils.h"
|
||||
#include "mozilla/dom/StaticRange.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsRange.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
|
||||
nsRange* aRange);
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
|
||||
nsRange* aRange);
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
|
||||
nsRange* aRange);
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RawRangeBoundary& aStartBoundary,
|
||||
const RawRangeBoundary& aEndBoundary, nsRange* aRange);
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
|
||||
StaticRange* aRange);
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
|
||||
StaticRange* aRange);
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
|
||||
StaticRange* aRange);
|
||||
template nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RawRangeBoundary& aStartBoundary,
|
||||
const RawRangeBoundary& aEndBoundary, StaticRange* aRange);
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractRange)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbstractRange)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AbstractRange)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractRange)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
|
||||
// mStart and mEnd may depend on or be depended on some other members in
|
||||
// concrete classes so that they should be unlinked in sub classes.
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AbstractRange)
|
||||
|
||||
AbstractRange::AbstractRange(nsINode* aNode)
|
||||
: mIsPositioned(false), mIsGenerated(false), mCalledByJS(false) {
|
||||
MOZ_ASSERT(aNode, "range isn't in a document!");
|
||||
mOwner = aNode->OwnerDoc();
|
||||
}
|
||||
|
||||
nsINode* AbstractRange::GetCommonAncestor() const {
|
||||
return mIsPositioned ? nsContentUtils::GetCommonAncestor(mStart.Container(),
|
||||
mEnd.Container())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
template <typename SPT, typename SRT, typename EPT, typename ERT,
|
||||
typename RangeType>
|
||||
nsresult AbstractRange::SetStartAndEndInternal(
|
||||
const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
|
||||
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange) {
|
||||
if (NS_WARN_IF(!aStartBoundary.IsSet()) ||
|
||||
NS_WARN_IF(!aEndBoundary.IsSet())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsINode* newStartRoot =
|
||||
RangeUtils::ComputeRootNode(aStartBoundary.Container());
|
||||
if (!newStartRoot) {
|
||||
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
|
||||
}
|
||||
if (!aStartBoundary.IsSetAndValid()) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
if (aStartBoundary.Container() == aEndBoundary.Container()) {
|
||||
if (!aEndBoundary.IsSetAndValid()) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
// XXX: Offsets - handle this more efficiently.
|
||||
// If the end offset is less than the start offset, this should be
|
||||
// collapsed at the end offset.
|
||||
if (aStartBoundary.Offset() > aEndBoundary.Offset()) {
|
||||
aRange->DoSetRange(aEndBoundary, aEndBoundary, newStartRoot);
|
||||
} else {
|
||||
aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsINode* newEndRoot = RangeUtils::ComputeRootNode(aEndBoundary.Container());
|
||||
if (!newEndRoot) {
|
||||
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
|
||||
}
|
||||
if (!aEndBoundary.IsSetAndValid()) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
// If they have different root, this should be collapsed at the end point.
|
||||
if (newStartRoot != newEndRoot) {
|
||||
aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If the end point is before the start point, this should be collapsed at
|
||||
// the end point.
|
||||
if (nsContentUtils::ComparePoints(aStartBoundary, aEndBoundary) == 1) {
|
||||
aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Otherwise, set the range as specified.
|
||||
aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject* AbstractRange::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
MOZ_CRASH("Must be overridden");
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
89
dom/base/AbstractRange.h
Normal file
89
dom/base/AbstractRange.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* 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_AbstractRange_h
|
||||
#define mozilla_dom_AbstractRange_h
|
||||
|
||||
#include "mozilla/RangeBoundary.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AbstractRange : public nsISupports, public nsWrapperCache {
|
||||
protected:
|
||||
explicit AbstractRange(nsINode* aNode);
|
||||
virtual ~AbstractRange() = default;
|
||||
|
||||
public:
|
||||
AbstractRange() = delete;
|
||||
explicit AbstractRange(const AbstractRange& aOther) = delete;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbstractRange)
|
||||
|
||||
const RangeBoundary& StartRef() const { return mStart; }
|
||||
const RangeBoundary& EndRef() const { return mEnd; }
|
||||
|
||||
nsIContent* GetChildAtStartOffset() const {
|
||||
return mStart.GetChildAtOffset();
|
||||
}
|
||||
nsIContent* GetChildAtEndOffset() const { return mEnd.GetChildAtOffset(); }
|
||||
bool IsPositioned() const { return mIsPositioned; }
|
||||
nsINode* GetCommonAncestor() const;
|
||||
|
||||
// WebIDL
|
||||
|
||||
// If Range is created from JS, it's initialized with Document.createRange()
|
||||
// and it collaps the range to start of the Document. Therefore, the
|
||||
// following WebIDL methods are called only when `mIsPositioned` is true.
|
||||
// So, it does not make sense to take `ErrorResult` as their parameter
|
||||
// since its destruction cost may appear in profile. If you create range
|
||||
// object from C++ and needs to check whether it's positioned, should call
|
||||
// `IsPositioned()` directly.
|
||||
|
||||
nsINode* GetStartContainer() const { return mStart.Container(); }
|
||||
nsINode* GetEndContainer() const { return mEnd.Container(); }
|
||||
uint32_t StartOffset() const {
|
||||
// FYI: Returns 0 if it's not positioned.
|
||||
return static_cast<uint32_t>(mStart.Offset());
|
||||
}
|
||||
uint32_t EndOffset() const {
|
||||
// FYI: Returns 0 if it's not positioned.
|
||||
return static_cast<uint32_t>(mEnd.Offset());
|
||||
}
|
||||
bool Collapsed() const {
|
||||
return !mIsPositioned || (mStart.Container() == mEnd.Container() &&
|
||||
mStart.Offset() == mEnd.Offset());
|
||||
}
|
||||
|
||||
nsINode* GetParentObject() const { return mOwner; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
protected:
|
||||
template <typename SPT, typename SRT, typename EPT, typename ERT,
|
||||
typename RangeType>
|
||||
static nsresult SetStartAndEndInternal(
|
||||
const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
|
||||
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange);
|
||||
|
||||
RefPtr<Document> mOwner;
|
||||
RangeBoundary mStart;
|
||||
RangeBoundary mEnd;
|
||||
bool mIsPositioned;
|
||||
|
||||
// Used by nsRange, but this should have this for minimizing the size.
|
||||
bool mIsGenerated;
|
||||
// Used by nsRange, but this should have this for minimizing the size.
|
||||
bool mCalledByJS;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef mozilla_dom_AbstractRange_h
|
@ -176,7 +176,7 @@ bool AnonymousContent::WrapObject(JSContext* aCx,
|
||||
}
|
||||
|
||||
void AnonymousContent::GetComputedStylePropertyValue(
|
||||
const nsAString& aElementId, const nsAString& aPropertyName,
|
||||
const nsAString& aElementId, const nsACString& aPropertyName,
|
||||
DOMString& aResult, ErrorResult& aRv) {
|
||||
Element* element = GetElementById(aElementId);
|
||||
if (!element) {
|
||||
@ -205,5 +205,17 @@ void AnonymousContent::GetTargetIdForEvent(Event& aEvent, DOMString& aResult) {
|
||||
aResult.SetNull();
|
||||
}
|
||||
|
||||
void AnonymousContent::SetStyle(const nsACString& aProperty,
|
||||
const nsACString& aValue, ErrorResult& aRv) {
|
||||
if (!mContentNode->IsHTMLElement()) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsGenericHTMLElement* element = nsGenericHTMLElement::FromNode(mContentNode);
|
||||
nsCOMPtr<nsICSSDeclaration> declaration = element->Style();
|
||||
declaration->SetProperty(aProperty, aValue, EmptyString());
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -64,11 +64,14 @@ class AnonymousContent final {
|
||||
ErrorResult& aError);
|
||||
|
||||
void GetComputedStylePropertyValue(const nsAString& aElementId,
|
||||
const nsAString& aPropertyName,
|
||||
const nsACString& aPropertyName,
|
||||
DOMString& aResult, ErrorResult& aRv);
|
||||
|
||||
void GetTargetIdForEvent(Event& aEvent, DOMString& aResult);
|
||||
|
||||
void SetStyle(const nsACString& aProperty, const nsACString& aValue,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
~AnonymousContent();
|
||||
RefPtr<Element> mContentNode;
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsNameSpaceManager.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "nsTextNode.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
@ -92,8 +91,7 @@ NS_INTERFACE_TABLE_HEAD(Attr)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Attr)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
|
||||
Attr, nsNodeUtils::LastRelease(this))
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Attr, LastRelease())
|
||||
|
||||
void Attr::SetMap(nsDOMAttributeMap* aMap) {
|
||||
if (mAttrMap && !aMap && sInitialized) {
|
||||
@ -180,7 +178,7 @@ nsresult Attr::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI> Attr::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
|
||||
nsIURI* Attr::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
|
||||
Element* parent = GetElement();
|
||||
|
||||
return parent ? parent->GetBaseURI(aTryUseXHRDocBaseURI) : nullptr;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define mozilla_dom_Attr_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@ -64,8 +65,7 @@ class Attr final : public nsINode {
|
||||
// nsINode interface
|
||||
virtual bool IsNodeOfType(uint32_t aFlags) const override;
|
||||
virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
|
||||
virtual already_AddRefed<nsIURI> GetBaseURI(
|
||||
bool aTryUseXHRDocBaseURI = false) const override;
|
||||
nsIURI* GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override;
|
||||
|
||||
static void Initialize();
|
||||
static void Shutdown();
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "mozilla/dom/BarPropBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIScrollable.h"
|
||||
#include "nsIWebBrowserChrome.h"
|
||||
|
||||
|
161
dom/base/BindContext.h
Normal file
161
dom/base/BindContext.h
Normal file
@ -0,0 +1,161 @@
|
||||
/* 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/. */
|
||||
|
||||
/* State that is passed down to BindToTree. */
|
||||
|
||||
#ifndef mozilla_dom_BindContext_h__
|
||||
#define mozilla_dom_BindContext_h__
|
||||
|
||||
#include "nsXBLBinding.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct MOZ_STACK_CLASS BindContext final {
|
||||
struct NestingLevel;
|
||||
friend struct NestingLevel;
|
||||
|
||||
// The document that owns the tree we're getting bound to.
|
||||
//
|
||||
// This is mostly an optimization to avoid silly pointer-chases to get the
|
||||
// OwnerDoc().
|
||||
Document& OwnerDoc() const { return mDoc; }
|
||||
|
||||
// Whether we're getting connected.
|
||||
//
|
||||
// https://dom.spec.whatwg.org/#connected
|
||||
bool InComposedDoc() const { return mInComposedDoc; }
|
||||
|
||||
// Whether we're getting bound to the document tree.
|
||||
//
|
||||
// https://dom.spec.whatwg.org/#in-a-document-tree
|
||||
bool InUncomposedDoc() const { return mInUncomposedDoc; }
|
||||
|
||||
Document* GetComposedDoc() const { return mInComposedDoc ? &mDoc : nullptr; }
|
||||
|
||||
Document* GetUncomposedDoc() const {
|
||||
return mInUncomposedDoc ? &mDoc : nullptr;
|
||||
}
|
||||
|
||||
// Whether our subtree root is changing as a result of this operation.
|
||||
bool SubtreeRootChanges() const { return mSubtreeRootChanges; }
|
||||
|
||||
// Returns the binding parent of the subtree to be inserted.
|
||||
//
|
||||
// This can be null.
|
||||
Element* GetBindingParent() const { return mBindingParent; }
|
||||
|
||||
// This constructor should be used for regular appends to content.
|
||||
explicit BindContext(nsINode& aParent)
|
||||
: mDoc(*aParent.OwnerDoc()),
|
||||
mBindingParent(aParent.IsContent()
|
||||
? aParent.AsContent()->GetBindingParent()
|
||||
: nullptr),
|
||||
mInComposedDoc(aParent.IsInComposedDoc()),
|
||||
mInUncomposedDoc(aParent.IsInUncomposedDoc()),
|
||||
mSubtreeRootChanges(true),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aParent)) {}
|
||||
|
||||
// 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
|
||||
// same subtree root. So children should avoid doing silly things like adding
|
||||
// themselves to the ShadowRoot's id table twice or what not.
|
||||
//
|
||||
// This constructor is only meant to be used in that situation.
|
||||
explicit BindContext(ShadowRoot& aShadowRoot)
|
||||
: mDoc(*aShadowRoot.OwnerDoc()),
|
||||
mBindingParent(aShadowRoot.Host()),
|
||||
mInComposedDoc(aShadowRoot.IsInComposedDoc()),
|
||||
mInUncomposedDoc(false),
|
||||
mSubtreeRootChanges(false),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aShadowRoot)) {}
|
||||
|
||||
// This constructor is meant to be used when inserting native-anonymous
|
||||
// children into a subtree.
|
||||
enum ForNativeAnonymous { ForNativeAnonymous };
|
||||
BindContext(Element& aParentElement, enum ForNativeAnonymous)
|
||||
: mDoc(*aParentElement.OwnerDoc()),
|
||||
mBindingParent(&aParentElement),
|
||||
mInComposedDoc(aParentElement.IsInComposedDoc()),
|
||||
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
|
||||
mSubtreeRootChanges(true),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aParentElement)) {
|
||||
MOZ_ASSERT(mInComposedDoc, "Binding NAC in a disconnected subtree?");
|
||||
}
|
||||
|
||||
// This is meant to be used to bind XBL anonymous content.
|
||||
BindContext(nsXBLBinding& aBinding, Element& aParentElement)
|
||||
: mDoc(*aParentElement.OwnerDoc()),
|
||||
mBindingParent(aBinding.GetBoundElement()),
|
||||
mInComposedDoc(aParentElement.IsInComposedDoc()),
|
||||
mInUncomposedDoc(aParentElement.IsInUncomposedDoc()),
|
||||
mSubtreeRootChanges(true),
|
||||
mCollectingDisplayedNodeDataDuringLoad(
|
||||
ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc,
|
||||
aParentElement)) {}
|
||||
|
||||
bool CollectingDisplayedNodeDataDuringLoad() const {
|
||||
return mCollectingDisplayedNodeDataDuringLoad;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool IsLikelyUndisplayed(const nsINode& aParent) {
|
||||
return aParent.IsAnyOfHTMLElements(nsGkAtoms::style, nsGkAtoms::script);
|
||||
}
|
||||
|
||||
static bool ShouldCollectDisplayedNodeDataDuringLoad(bool aConnected,
|
||||
Document& aDoc,
|
||||
nsINode& aParent) {
|
||||
return aDoc.GetReadyStateEnum() == Document::READYSTATE_LOADING &&
|
||||
aConnected && !IsLikelyUndisplayed(aParent);
|
||||
}
|
||||
|
||||
Document& mDoc;
|
||||
|
||||
Element* const mBindingParent;
|
||||
|
||||
const bool mInComposedDoc;
|
||||
const bool mInUncomposedDoc;
|
||||
|
||||
// Whether the bind operation will change the subtree root of the content
|
||||
// we're binding.
|
||||
const bool mSubtreeRootChanges;
|
||||
|
||||
// Whether it's likely that we're in an undisplayed part of the DOM.
|
||||
//
|
||||
// NOTE(emilio): We don't inherit this in BindContext's for Shadow DOM or XBL
|
||||
// or such. This means that if you have a shadow tree inside an undisplayed
|
||||
// element it will be incorrectly counted. But given our current definition
|
||||
// of undisplayed element this is not likely to matter in practice.
|
||||
bool mCollectingDisplayedNodeDataDuringLoad;
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS BindContext::NestingLevel {
|
||||
explicit NestingLevel(BindContext& aContext, const Element& aParent)
|
||||
: mRestoreCollecting(aContext.mCollectingDisplayedNodeDataDuringLoad) {
|
||||
if (aContext.mCollectingDisplayedNodeDataDuringLoad) {
|
||||
aContext.mCollectingDisplayedNodeDataDuringLoad =
|
||||
BindContext::IsLikelyUndisplayed(aParent);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AutoRestore<bool> mRestoreCollecting;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -2,14 +2,16 @@
|
||||
* 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 "Fetch.h"
|
||||
#include "FetchConsumer.h"
|
||||
#include "BodyConsumer.h"
|
||||
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/BlobURLProtocolHandler.h"
|
||||
#include "mozilla/dom/BodyUtil.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FileBinding.h"
|
||||
#include "mozilla/dom/FileCreatorHelper.h"
|
||||
#include "mozilla/dom/MutableBlobStreamListener.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
@ -17,8 +19,9 @@
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIThreadRetargetableRequest.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
// Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
|
||||
@ -32,42 +35,40 @@ namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Derived>
|
||||
class BeginConsumeBodyRunnable final : public Runnable {
|
||||
public:
|
||||
BeginConsumeBodyRunnable(FetchBodyConsumer<Derived>* aConsumer,
|
||||
BeginConsumeBodyRunnable(BodyConsumer* aConsumer,
|
||||
ThreadSafeWorkerRef* aWorkerRef)
|
||||
: Runnable("BeginConsumeBodyRunnable"),
|
||||
mFetchBodyConsumer(aConsumer),
|
||||
mBodyConsumer(aConsumer),
|
||||
mWorkerRef(aWorkerRef) {}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override {
|
||||
mFetchBodyConsumer->BeginConsumeBodyMainThread(mWorkerRef);
|
||||
mBodyConsumer->BeginConsumeBodyMainThread(mWorkerRef);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
RefPtr<BodyConsumer> mBodyConsumer;
|
||||
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
|
||||
};
|
||||
|
||||
/*
|
||||
* Called on successfully reading the complete stream.
|
||||
*/
|
||||
template <class Derived>
|
||||
class ContinueConsumeBodyRunnable final : public MainThreadWorkerRunnable {
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
RefPtr<BodyConsumer> mBodyConsumer;
|
||||
nsresult mStatus;
|
||||
uint32_t mLength;
|
||||
uint8_t* mResult;
|
||||
|
||||
public:
|
||||
ContinueConsumeBodyRunnable(FetchBodyConsumer<Derived>* aFetchBodyConsumer,
|
||||
ContinueConsumeBodyRunnable(BodyConsumer* aBodyConsumer,
|
||||
WorkerPrivate* aWorkerPrivate, nsresult aStatus,
|
||||
uint32_t aLength, uint8_t* aResult)
|
||||
: MainThreadWorkerRunnable(aWorkerPrivate),
|
||||
mFetchBodyConsumer(aFetchBodyConsumer),
|
||||
mBodyConsumer(aBodyConsumer),
|
||||
mStatus(aStatus),
|
||||
mLength(aLength),
|
||||
mResult(aResult) {
|
||||
@ -75,29 +76,27 @@ class ContinueConsumeBodyRunnable final : public MainThreadWorkerRunnable {
|
||||
}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
|
||||
mFetchBodyConsumer->ContinueConsumeBody(mStatus, mLength, mResult);
|
||||
mBodyConsumer->ContinueConsumeBody(mStatus, mLength, mResult);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// ControlRunnable used to complete the releasing of resources on the worker
|
||||
// thread when already shutting down.
|
||||
template <class Derived>
|
||||
class AbortConsumeBodyControlRunnable final
|
||||
: public MainThreadWorkerControlRunnable {
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
RefPtr<BodyConsumer> mBodyConsumer;
|
||||
|
||||
public:
|
||||
AbortConsumeBodyControlRunnable(
|
||||
FetchBodyConsumer<Derived>* aFetchBodyConsumer,
|
||||
AbortConsumeBodyControlRunnable(BodyConsumer* aBodyConsumer,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: MainThreadWorkerControlRunnable(aWorkerPrivate),
|
||||
mFetchBodyConsumer(aFetchBodyConsumer) {
|
||||
mBodyConsumer(aBodyConsumer) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
|
||||
mFetchBodyConsumer->ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr,
|
||||
mBodyConsumer->ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr,
|
||||
true /* shutting down */);
|
||||
return true;
|
||||
}
|
||||
@ -107,10 +106,9 @@ class AbortConsumeBodyControlRunnable final
|
||||
* In case of failure to create a stream pump or dispatch stream completion to
|
||||
* worker, ensure we cleanup properly. Thread agnostic.
|
||||
*/
|
||||
template <class Derived>
|
||||
class MOZ_STACK_CLASS AutoFailConsumeBody final {
|
||||
public:
|
||||
AutoFailConsumeBody(FetchBodyConsumer<Derived>* aBodyConsumer,
|
||||
AutoFailConsumeBody(BodyConsumer* aBodyConsumer,
|
||||
ThreadSafeWorkerRef* aWorkerRef)
|
||||
: mBodyConsumer(aBodyConsumer), mWorkerRef(aWorkerRef) {}
|
||||
|
||||
@ -123,8 +121,8 @@ class MOZ_STACK_CLASS AutoFailConsumeBody final {
|
||||
|
||||
// Web Worker
|
||||
if (mWorkerRef) {
|
||||
RefPtr<AbortConsumeBodyControlRunnable<Derived>> r =
|
||||
new AbortConsumeBodyControlRunnable<Derived>(mBodyConsumer,
|
||||
RefPtr<AbortConsumeBodyControlRunnable> r =
|
||||
new AbortConsumeBodyControlRunnable(mBodyConsumer,
|
||||
mWorkerRef->Private());
|
||||
if (!r->Dispatch()) {
|
||||
MOZ_CRASH("We are going to leak");
|
||||
@ -139,67 +137,62 @@ class MOZ_STACK_CLASS AutoFailConsumeBody final {
|
||||
void DontFail() { mBodyConsumer = nullptr; }
|
||||
|
||||
private:
|
||||
RefPtr<FetchBodyConsumer<Derived>> mBodyConsumer;
|
||||
RefPtr<BodyConsumer> mBodyConsumer;
|
||||
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
|
||||
};
|
||||
|
||||
/*
|
||||
* Called on successfully reading the complete stream for Blob.
|
||||
*/
|
||||
template <class Derived>
|
||||
class ContinueConsumeBlobBodyRunnable final : public MainThreadWorkerRunnable {
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
RefPtr<BodyConsumer> mBodyConsumer;
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
|
||||
public:
|
||||
ContinueConsumeBlobBodyRunnable(
|
||||
FetchBodyConsumer<Derived>* aFetchBodyConsumer,
|
||||
WorkerPrivate* aWorkerPrivate, BlobImpl* aBlobImpl)
|
||||
ContinueConsumeBlobBodyRunnable(BodyConsumer* aBodyConsumer,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
BlobImpl* aBlobImpl)
|
||||
: MainThreadWorkerRunnable(aWorkerPrivate),
|
||||
mFetchBodyConsumer(aFetchBodyConsumer),
|
||||
mBodyConsumer(aBodyConsumer),
|
||||
mBlobImpl(aBlobImpl) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobImpl);
|
||||
}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
|
||||
mFetchBodyConsumer->ContinueConsumeBlobBody(mBlobImpl);
|
||||
mBodyConsumer->ContinueConsumeBlobBody(mBlobImpl);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// ControlRunnable used to complete the releasing of resources on the worker
|
||||
// thread when already shutting down.
|
||||
template <class Derived>
|
||||
class AbortConsumeBlobBodyControlRunnable final
|
||||
: public MainThreadWorkerControlRunnable {
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
RefPtr<BodyConsumer> mBodyConsumer;
|
||||
|
||||
public:
|
||||
AbortConsumeBlobBodyControlRunnable(
|
||||
FetchBodyConsumer<Derived>* aFetchBodyConsumer,
|
||||
AbortConsumeBlobBodyControlRunnable(BodyConsumer* aBodyConsumer,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: MainThreadWorkerControlRunnable(aWorkerPrivate),
|
||||
mFetchBodyConsumer(aFetchBodyConsumer) {
|
||||
mBodyConsumer(aBodyConsumer) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
|
||||
mFetchBodyConsumer->ContinueConsumeBlobBody(nullptr,
|
||||
true /* shutting down */);
|
||||
mBodyConsumer->ContinueConsumeBlobBody(nullptr, true /* shutting down */);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
|
||||
public MutableBlobStorageCallback {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
ConsumeBodyDoneObserver(FetchBodyConsumer<Derived>* aFetchBodyConsumer,
|
||||
ConsumeBodyDoneObserver(BodyConsumer* aBodyConsumer,
|
||||
ThreadSafeWorkerRef* aWorkerRef)
|
||||
: mFetchBodyConsumer(aFetchBodyConsumer), mWorkerRef(aWorkerRef) {}
|
||||
: mBodyConsumer(aBodyConsumer), mWorkerRef(aWorkerRef) {}
|
||||
|
||||
NS_IMETHOD
|
||||
OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aCtxt,
|
||||
@ -209,26 +202,25 @@ class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
|
||||
|
||||
// The loading is completed. Let's nullify the pump before continuing the
|
||||
// consuming of the body.
|
||||
mFetchBodyConsumer->NullifyConsumeBodyPump();
|
||||
mBodyConsumer->NullifyConsumeBodyPump();
|
||||
|
||||
uint8_t* nonconstResult = const_cast<uint8_t*>(aResult);
|
||||
|
||||
// Main-thread.
|
||||
if (!mWorkerRef) {
|
||||
mFetchBodyConsumer->ContinueConsumeBody(aStatus, aResultLength,
|
||||
mBodyConsumer->ContinueConsumeBody(aStatus, aResultLength,
|
||||
nonconstResult);
|
||||
// FetchBody is responsible for data.
|
||||
// The caller is responsible for data.
|
||||
return NS_SUCCESS_ADOPTED_DATA;
|
||||
}
|
||||
|
||||
// Web Worker.
|
||||
{
|
||||
RefPtr<ContinueConsumeBodyRunnable<Derived>> r =
|
||||
new ContinueConsumeBodyRunnable<Derived>(
|
||||
mFetchBodyConsumer, mWorkerRef->Private(), aStatus, aResultLength,
|
||||
RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
|
||||
mBodyConsumer, mWorkerRef->Private(), aStatus, aResultLength,
|
||||
nonconstResult);
|
||||
if (r->Dispatch()) {
|
||||
// FetchBody is responsible for data.
|
||||
// The caller is responsible for data.
|
||||
return NS_SUCCESS_ADOPTED_DATA;
|
||||
}
|
||||
}
|
||||
@ -236,8 +228,8 @@ class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
|
||||
// The worker is shutting down. Let's use a control runnable to complete the
|
||||
// shutting down procedure.
|
||||
|
||||
RefPtr<AbortConsumeBodyControlRunnable<Derived>> r =
|
||||
new AbortConsumeBodyControlRunnable<Derived>(mFetchBodyConsumer,
|
||||
RefPtr<AbortConsumeBodyControlRunnable> r =
|
||||
new AbortConsumeBodyControlRunnable(mBodyConsumer,
|
||||
mWorkerRef->Private());
|
||||
if (NS_WARN_IF(!r->Dispatch())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -257,36 +249,29 @@ class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
|
||||
|
||||
// The loading is completed. Let's nullify the pump before continuing the
|
||||
// consuming of the body.
|
||||
mFetchBodyConsumer->NullifyConsumeBodyPump();
|
||||
mBodyConsumer->NullifyConsumeBodyPump();
|
||||
|
||||
mFetchBodyConsumer->OnBlobResult(aBlob, mWorkerRef);
|
||||
mBodyConsumer->OnBlobResult(aBlob, mWorkerRef);
|
||||
}
|
||||
|
||||
private:
|
||||
~ConsumeBodyDoneObserver() = default;
|
||||
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
RefPtr<BodyConsumer> mBodyConsumer;
|
||||
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_ADDREF(ConsumeBodyDoneObserver<Derived>)
|
||||
template <class Derived>
|
||||
NS_IMPL_RELEASE(ConsumeBodyDoneObserver<Derived>)
|
||||
template <class Derived>
|
||||
NS_INTERFACE_MAP_BEGIN(ConsumeBodyDoneObserver<Derived>)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamLoaderObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver, nsIStreamLoaderObserver)
|
||||
|
||||
} // anonymous
|
||||
} // namespace
|
||||
|
||||
template <class Derived>
|
||||
/* static */ already_AddRefed<Promise> FetchBodyConsumer<Derived>::Create(
|
||||
/* static */ already_AddRefed<Promise> BodyConsumer::Create(
|
||||
nsIGlobalObject* aGlobal, nsIEventTarget* aMainThreadEventTarget,
|
||||
FetchBody<Derived>* aBody, nsIInputStream* aBodyStream,
|
||||
AbortSignalImpl* aSignalImpl, FetchConsumeType aType, ErrorResult& aRv) {
|
||||
MOZ_ASSERT(aBody);
|
||||
nsIInputStream* aBodyStream, AbortSignalImpl* aSignalImpl,
|
||||
ConsumeType aType, const nsACString& aBodyBlobURISpec,
|
||||
const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType,
|
||||
MutableBlobStorage::MutableBlobStorageType aBlobStorageType,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_ASSERT(aBodyStream);
|
||||
MOZ_ASSERT(aMainThreadEventTarget);
|
||||
|
||||
@ -295,8 +280,9 @@ template <class Derived>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<FetchBodyConsumer<Derived>> consumer = new FetchBodyConsumer<Derived>(
|
||||
aMainThreadEventTarget, aGlobal, aBody, aBodyStream, promise, aType);
|
||||
RefPtr<BodyConsumer> consumer = new BodyConsumer(
|
||||
aMainThreadEventTarget, aGlobal, aBodyStream, promise, aType,
|
||||
aBodyBlobURISpec, aBodyLocalPath, aBodyMimeType, aBlobStorageType);
|
||||
|
||||
RefPtr<ThreadSafeWorkerRef> workerRef;
|
||||
|
||||
@ -305,7 +291,7 @@ template <class Derived>
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
|
||||
workerPrivate, "FetchBodyConsumer",
|
||||
workerPrivate, "BodyConsumer",
|
||||
[consumer]() { consumer->ShutDownMainThreadConsuming(); });
|
||||
if (NS_WARN_IF(!strongWorkerRef)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
@ -331,8 +317,7 @@ template <class Derived>
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new BeginConsumeBodyRunnable<Derived>(consumer, workerRef);
|
||||
nsCOMPtr<nsIRunnable> r = new BeginConsumeBodyRunnable(consumer, workerRef);
|
||||
aRv = aMainThreadEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
@ -345,8 +330,7 @@ template <class Derived>
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::ReleaseObject() {
|
||||
void BodyConsumer::ReleaseObject() {
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
@ -359,78 +343,51 @@ void FetchBodyConsumer<Derived>::ReleaseObject() {
|
||||
|
||||
mGlobal = nullptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
mBody = nullptr;
|
||||
#endif
|
||||
|
||||
Unfollow();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
FetchBodyConsumer<Derived>::FetchBodyConsumer(
|
||||
BodyConsumer::BodyConsumer(
|
||||
nsIEventTarget* aMainThreadEventTarget, nsIGlobalObject* aGlobalObject,
|
||||
FetchBody<Derived>* aBody, nsIInputStream* aBodyStream, Promise* aPromise,
|
||||
FetchConsumeType aType)
|
||||
nsIInputStream* aBodyStream, Promise* aPromise, ConsumeType aType,
|
||||
const nsACString& aBodyBlobURISpec, const nsAString& aBodyLocalPath,
|
||||
const nsACString& aBodyMimeType,
|
||||
MutableBlobStorage::MutableBlobStorageType aBlobStorageType)
|
||||
: mTargetThread(NS_GetCurrentThread()),
|
||||
mMainThreadEventTarget(aMainThreadEventTarget)
|
||||
#ifdef DEBUG
|
||||
,
|
||||
mBody(aBody)
|
||||
#endif
|
||||
,
|
||||
mMainThreadEventTarget(aMainThreadEventTarget),
|
||||
mBodyStream(aBodyStream),
|
||||
mBlobStorageType(MutableBlobStorage::eOnlyInMemory),
|
||||
mBodyBlobURISpec(aBody ? aBody->BodyBlobURISpec() : VoidCString()),
|
||||
mBodyLocalPath(aBody ? aBody->BodyLocalPath() : VoidString()),
|
||||
mBlobStorageType(aBlobStorageType),
|
||||
mBodyMimeType(aBodyMimeType),
|
||||
mBodyBlobURISpec(aBodyBlobURISpec),
|
||||
mBodyLocalPath(aBodyLocalPath),
|
||||
mGlobal(aGlobalObject),
|
||||
mConsumeType(aType),
|
||||
mConsumePromise(aPromise),
|
||||
mBodyConsumed(false),
|
||||
mShuttingDown(false) {
|
||||
MOZ_ASSERT(aMainThreadEventTarget);
|
||||
MOZ_ASSERT(aBody);
|
||||
MOZ_ASSERT(aBodyStream);
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
|
||||
aBody->DerivedClass()->GetPrincipalInfo();
|
||||
// We support temporary file for blobs only if the principal is known and
|
||||
// it's system or content not in private Browsing.
|
||||
if (principalInfo &&
|
||||
(principalInfo->type() ==
|
||||
mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo ||
|
||||
(principalInfo->type() ==
|
||||
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
|
||||
principalInfo->get_ContentPrincipalInfo().attrs().mPrivateBrowsingId ==
|
||||
0))) {
|
||||
mBlobStorageType = MutableBlobStorage::eCouldBeInTemporaryFile;
|
||||
}
|
||||
|
||||
mBodyMimeType = aBody->MimeType();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
FetchBodyConsumer<Derived>::~FetchBodyConsumer() {}
|
||||
BodyConsumer::~BodyConsumer() = default;
|
||||
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::AssertIsOnTargetThread() const {
|
||||
void BodyConsumer::AssertIsOnTargetThread() const {
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mTargetThread);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Derived>
|
||||
class FileCreationHandler final : public PromiseNativeHandler {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
static void Create(Promise* aPromise, FetchBodyConsumer<Derived>* aConsumer,
|
||||
static void Create(Promise* aPromise, BodyConsumer* aConsumer,
|
||||
ThreadSafeWorkerRef* aWorkerRef) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
RefPtr<FileCreationHandler> handler =
|
||||
new FileCreationHandler<Derived>(aConsumer, aWorkerRef);
|
||||
new FileCreationHandler(aConsumer, aWorkerRef);
|
||||
aPromise->AppendNativeHandler(handler);
|
||||
}
|
||||
|
||||
@ -458,8 +415,7 @@ class FileCreationHandler final : public PromiseNativeHandler {
|
||||
}
|
||||
|
||||
private:
|
||||
FileCreationHandler<Derived>(FetchBodyConsumer<Derived>* aConsumer,
|
||||
ThreadSafeWorkerRef* aWorkerRef)
|
||||
FileCreationHandler(BodyConsumer* aConsumer, ThreadSafeWorkerRef* aWorkerRef)
|
||||
: mConsumer(aConsumer), mWorkerRef(aWorkerRef) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aConsumer);
|
||||
@ -467,23 +423,15 @@ class FileCreationHandler final : public PromiseNativeHandler {
|
||||
|
||||
~FileCreationHandler() = default;
|
||||
|
||||
RefPtr<FetchBodyConsumer<Derived>> mConsumer;
|
||||
RefPtr<BodyConsumer> mConsumer;
|
||||
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_ADDREF(FileCreationHandler<Derived>)
|
||||
template <class Derived>
|
||||
NS_IMPL_RELEASE(FileCreationHandler<Derived>)
|
||||
template <class Derived>
|
||||
NS_INTERFACE_MAP_BEGIN(FileCreationHandler<Derived>)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
NS_IMPL_ISUPPORTS0(FileCreationHandler)
|
||||
|
||||
} // namespace
|
||||
|
||||
template <class Derived>
|
||||
nsresult FetchBodyConsumer<Derived>::GetBodyLocalFile(nsIFile** aFile) const {
|
||||
nsresult BodyConsumer::GetBodyLocalFile(nsIFile** aFile) const {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!mBodyLocalPath.Length()) {
|
||||
@ -522,12 +470,10 @@ nsresult FetchBodyConsumer<Derived>::GetBodyLocalFile(nsIFile** aFile) const {
|
||||
* and clean up on any failures, so there is no need for callers to do so,
|
||||
* reflected in a lack of error return code.
|
||||
*/
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::BeginConsumeBodyMainThread(
|
||||
ThreadSafeWorkerRef* aWorkerRef) {
|
||||
void BodyConsumer::BeginConsumeBodyMainThread(ThreadSafeWorkerRef* aWorkerRef) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
AutoFailConsumeBody<Derived> autoReject(this, aWorkerRef);
|
||||
AutoFailConsumeBody autoReject(this, aWorkerRef);
|
||||
|
||||
if (mShuttingDown) {
|
||||
// We haven't started yet, but we have been terminated. AutoFailConsumeBody
|
||||
@ -567,7 +513,7 @@ void FetchBodyConsumer<Derived>::BeginConsumeBodyMainThread(
|
||||
}
|
||||
|
||||
autoReject.DontFail();
|
||||
FileCreationHandler<Derived>::Create(promise, this, aWorkerRef);
|
||||
FileCreationHandler::Create(promise, this, aWorkerRef);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -580,8 +526,8 @@ void FetchBodyConsumer<Derived>::BeginConsumeBodyMainThread(
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ConsumeBodyDoneObserver<Derived>> p =
|
||||
new ConsumeBodyDoneObserver<Derived>(this, aWorkerRef);
|
||||
RefPtr<ConsumeBodyDoneObserver> p =
|
||||
new ConsumeBodyDoneObserver(this, aWorkerRef);
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
if (mConsumeType == CONSUME_BLOB) {
|
||||
@ -603,7 +549,7 @@ void FetchBodyConsumer<Derived>::BeginConsumeBodyMainThread(
|
||||
}
|
||||
|
||||
// Now that everything succeeded, we can assign the pump to a pointer that
|
||||
// stays alive for the lifetime of the FetchConsumer.
|
||||
// stays alive for the lifetime of the BodyConsumer.
|
||||
mConsumeBodyPump = pump;
|
||||
|
||||
// It is ok for retargeting to fail and reads to happen on the main thread.
|
||||
@ -627,16 +573,13 @@ void FetchBodyConsumer<Derived>::BeginConsumeBodyMainThread(
|
||||
* been wrapped by FileCreationHandler). The blob is sent to the target thread
|
||||
* and ContinueConsumeBody is called.
|
||||
*/
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::OnBlobResult(Blob* aBlob,
|
||||
ThreadSafeWorkerRef* aWorkerRef) {
|
||||
void BodyConsumer::OnBlobResult(Blob* aBlob, ThreadSafeWorkerRef* aWorkerRef) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
DispatchContinueConsumeBlobBody(aBlob ? aBlob->Impl() : nullptr, aWorkerRef);
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::DispatchContinueConsumeBlobBody(
|
||||
void BodyConsumer::DispatchContinueConsumeBlobBody(
|
||||
BlobImpl* aBlobImpl, ThreadSafeWorkerRef* aWorkerRef) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@ -652,16 +595,15 @@ void FetchBodyConsumer<Derived>::DispatchContinueConsumeBlobBody(
|
||||
|
||||
// Web Worker.
|
||||
if (aBlobImpl) {
|
||||
RefPtr<ContinueConsumeBlobBodyRunnable<Derived>> r =
|
||||
new ContinueConsumeBlobBodyRunnable<Derived>(
|
||||
this, aWorkerRef->Private(), aBlobImpl);
|
||||
RefPtr<ContinueConsumeBlobBodyRunnable> r =
|
||||
new ContinueConsumeBlobBodyRunnable(this, aWorkerRef->Private(),
|
||||
aBlobImpl);
|
||||
|
||||
if (r->Dispatch()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
RefPtr<ContinueConsumeBodyRunnable<Derived>> r =
|
||||
new ContinueConsumeBodyRunnable<Derived>(
|
||||
RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
|
||||
this, aWorkerRef->Private(), NS_ERROR_DOM_ABORT_ERR, 0, nullptr);
|
||||
|
||||
if (r->Dispatch()) {
|
||||
@ -672,9 +614,8 @@ void FetchBodyConsumer<Derived>::DispatchContinueConsumeBlobBody(
|
||||
// The worker is shutting down. Let's use a control runnable to complete the
|
||||
// shutting down procedure.
|
||||
|
||||
RefPtr<AbortConsumeBlobBodyControlRunnable<Derived>> r =
|
||||
new AbortConsumeBlobBodyControlRunnable<Derived>(this,
|
||||
aWorkerRef->Private());
|
||||
RefPtr<AbortConsumeBlobBodyControlRunnable> r =
|
||||
new AbortConsumeBlobBodyControlRunnable(this, aWorkerRef->Private());
|
||||
|
||||
Unused << NS_WARN_IF(!r->Dispatch());
|
||||
}
|
||||
@ -685,11 +626,8 @@ void FetchBodyConsumer<Derived>::DispatchContinueConsumeBlobBody(
|
||||
* rejected based on whether the fetch succeeded, and the body can be
|
||||
* converted into the expected type of JS object.
|
||||
*/
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::ContinueConsumeBody(nsresult aStatus,
|
||||
uint32_t aResultLength,
|
||||
uint8_t* aResult,
|
||||
bool aShuttingDown) {
|
||||
void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
|
||||
uint8_t* aResult, bool aShuttingDown) {
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
// This makes sure that we free the data correctly.
|
||||
@ -700,14 +638,10 @@ void FetchBodyConsumer<Derived>::ContinueConsumeBody(nsresult aStatus,
|
||||
}
|
||||
mBodyConsumed = true;
|
||||
|
||||
// Just a precaution to ensure ContinueConsumeBody is not called out of
|
||||
// sync with a body read.
|
||||
MOZ_ASSERT(mBody->CheckBodyUsed());
|
||||
|
||||
MOZ_ASSERT(mConsumePromise);
|
||||
RefPtr<Promise> localPromise = mConsumePromise.forget();
|
||||
|
||||
RefPtr<FetchBodyConsumer<Derived>> self = this;
|
||||
RefPtr<BodyConsumer> self = this;
|
||||
auto autoReleaseObject =
|
||||
mozilla::MakeScopeExit([self] { self->ReleaseObject(); });
|
||||
|
||||
@ -721,9 +655,7 @@ void FetchBodyConsumer<Derived>::ContinueConsumeBody(nsresult aStatus,
|
||||
// https://fetch.spec.whatwg.org/#concept-read-all-bytes-from-readablestream
|
||||
// Decoding errors should reject with a TypeError
|
||||
if (aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
|
||||
IgnoredErrorResult rv;
|
||||
rv.ThrowTypeError<MSG_DOM_DECODING_FAILED>();
|
||||
localPromise->MaybeReject(rv);
|
||||
localPromise->MaybeRejectWithTypeError<MSG_DOM_DECODING_FAILED>();
|
||||
} else {
|
||||
localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
@ -806,8 +738,7 @@ void FetchBodyConsumer<Derived>::ContinueConsumeBody(nsresult aStatus,
|
||||
}
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::ContinueConsumeBlobBody(BlobImpl* aBlobImpl,
|
||||
void BodyConsumer::ContinueConsumeBlobBody(BlobImpl* aBlobImpl,
|
||||
bool aShuttingDown) {
|
||||
AssertIsOnTargetThread();
|
||||
MOZ_ASSERT(mConsumeType == CONSUME_BLOB);
|
||||
@ -817,10 +748,6 @@ void FetchBodyConsumer<Derived>::ContinueConsumeBlobBody(BlobImpl* aBlobImpl,
|
||||
}
|
||||
mBodyConsumed = true;
|
||||
|
||||
// Just a precaution to ensure ContinueConsumeBody is not called out of
|
||||
// sync with a body read.
|
||||
MOZ_ASSERT(mBody->CheckBodyUsed());
|
||||
|
||||
MOZ_ASSERT(mConsumePromise);
|
||||
RefPtr<Promise> localPromise = mConsumePromise.forget();
|
||||
|
||||
@ -834,13 +761,12 @@ void FetchBodyConsumer<Derived>::ContinueConsumeBlobBody(BlobImpl* aBlobImpl,
|
||||
ReleaseObject();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::ShutDownMainThreadConsuming() {
|
||||
void BodyConsumer::ShutDownMainThreadConsuming() {
|
||||
if (!NS_IsMainThread()) {
|
||||
RefPtr<FetchBodyConsumer<Derived>> self = this;
|
||||
RefPtr<BodyConsumer> self = this;
|
||||
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
"FetchBodyConsumer::ShutDownMainThreadConsuming",
|
||||
"BodyConsumer::ShutDownMainThreadConsuming",
|
||||
[self]() { self->ShutDownMainThreadConsuming(); });
|
||||
|
||||
mMainThreadEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
@ -857,9 +783,7 @@ void FetchBodyConsumer<Derived>::ShutDownMainThreadConsuming() {
|
||||
}
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
NS_IMETHODIMP FetchBodyConsumer<Derived>::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
NS_IMETHODIMP BodyConsumer::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@ -874,22 +798,13 @@ NS_IMETHODIMP FetchBodyConsumer<Derived>::Observe(nsISupports* aSubject,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void FetchBodyConsumer<Derived>::Abort() {
|
||||
void BodyConsumer::Abort() {
|
||||
AssertIsOnTargetThread();
|
||||
ShutDownMainThreadConsuming();
|
||||
ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR, 0, nullptr);
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_ADDREF(FetchBodyConsumer<Derived>)
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_RELEASE(FetchBodyConsumer<Derived>)
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_QUERY_INTERFACE(FetchBodyConsumer<Derived>, nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
NS_IMPL_ISUPPORTS(BodyConsumer, nsIObserver, nsISupportsWeakReference)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
136
dom/base/BodyConsumer.h
Normal file
136
dom/base/BodyConsumer.h
Normal file
@ -0,0 +1,136 @@
|
||||
/* 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_BodyConsumer_h
|
||||
#define mozilla_dom_BodyConsumer_h
|
||||
|
||||
#include "mozilla/dom/AbortSignal.h"
|
||||
#include "mozilla/dom/MutableBlobStorage.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class ThreadSafeWorkerRef;
|
||||
|
||||
// In order to keep alive the object all the time, we use a ThreadSafeWorkerRef,
|
||||
// if created on workers.
|
||||
class BodyConsumer final : public nsIObserver,
|
||||
public nsSupportsWeakReference,
|
||||
public AbortFollower {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
enum ConsumeType {
|
||||
CONSUME_ARRAYBUFFER,
|
||||
CONSUME_BLOB,
|
||||
CONSUME_FORMDATA,
|
||||
CONSUME_JSON,
|
||||
CONSUME_TEXT,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise which will be resolved when the body is completely
|
||||
* consumed and converted to the wanted type (See ConsumeType).
|
||||
*
|
||||
* @param aGlobal the global to construct the Promise.
|
||||
* @param aMainThreadEventTarget the main-thread event target. The reading
|
||||
* needs to start on the main-thread because of nsIInputStreamPump.
|
||||
* @param aBodyStream the stream to read.
|
||||
* @param aSignalImpl an AbortSignal object. Optional.
|
||||
* @param aType the consume type.
|
||||
* @param aBodyBlobURISpec this is used only if the consume type is
|
||||
* CONSUME_BLOB. Optional.
|
||||
* @param aBodyLocalPath local path in case the blob is created from a local
|
||||
* file. Used only by CONSUME_BLOB. Optional.
|
||||
* @param aBodyMimeType the mime-type for blob. Used only by CONSUME_BLOB.
|
||||
* Optional.
|
||||
* @param aBlobStorageType Blobs can be saved in temporary file. This is the
|
||||
* type of blob storage to use. Used only by CONSUME_BLOB.
|
||||
* @param aRv An ErrorResult.
|
||||
*/
|
||||
static already_AddRefed<Promise> Create(
|
||||
nsIGlobalObject* aGlobal, nsIEventTarget* aMainThreadEventTarget,
|
||||
nsIInputStream* aBodyStream, AbortSignalImpl* aSignalImpl,
|
||||
ConsumeType aType, const nsACString& aBodyBlobURISpec,
|
||||
const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType,
|
||||
MutableBlobStorage::MutableBlobStorageType aBlobStorageType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void ReleaseObject();
|
||||
|
||||
void BeginConsumeBodyMainThread(ThreadSafeWorkerRef* aWorkerRef);
|
||||
|
||||
void OnBlobResult(Blob* aBlob, ThreadSafeWorkerRef* aWorkerRef = nullptr);
|
||||
|
||||
void ContinueConsumeBody(nsresult aStatus, uint32_t aLength, uint8_t* aResult,
|
||||
bool aShuttingDown = false);
|
||||
|
||||
void ContinueConsumeBlobBody(BlobImpl* aBlobImpl, bool aShuttingDown = false);
|
||||
|
||||
void DispatchContinueConsumeBlobBody(BlobImpl* aBlobImpl,
|
||||
ThreadSafeWorkerRef* aWorkerRef);
|
||||
|
||||
void ShutDownMainThreadConsuming();
|
||||
|
||||
void NullifyConsumeBodyPump() {
|
||||
mShuttingDown = true;
|
||||
mConsumeBodyPump = nullptr;
|
||||
}
|
||||
|
||||
// AbortFollower
|
||||
void Abort() override;
|
||||
|
||||
private:
|
||||
BodyConsumer(nsIEventTarget* aMainThreadEventTarget,
|
||||
nsIGlobalObject* aGlobalObject, nsIInputStream* aBodyStream,
|
||||
Promise* aPromise, ConsumeType aType,
|
||||
const nsACString& aBodyBlobURISpec,
|
||||
const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType,
|
||||
MutableBlobStorage::MutableBlobStorageType aBlobStorageType);
|
||||
|
||||
~BodyConsumer();
|
||||
|
||||
nsresult GetBodyLocalFile(nsIFile** aFile) const;
|
||||
|
||||
void AssertIsOnTargetThread() const;
|
||||
|
||||
nsCOMPtr<nsIThread> mTargetThread;
|
||||
nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
|
||||
|
||||
// This is nullified when the consuming of the body starts.
|
||||
nsCOMPtr<nsIInputStream> mBodyStream;
|
||||
|
||||
MutableBlobStorage::MutableBlobStorageType mBlobStorageType;
|
||||
nsCString mBodyMimeType;
|
||||
|
||||
nsCString mBodyBlobURISpec;
|
||||
nsString mBodyLocalPath;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
|
||||
// Touched on the main-thread only.
|
||||
nsCOMPtr<nsIInputStreamPump> mConsumeBodyPump;
|
||||
|
||||
// Only ever set once, always on target thread.
|
||||
ConsumeType mConsumeType;
|
||||
RefPtr<Promise> mConsumePromise;
|
||||
|
||||
// touched only on the target thread.
|
||||
bool mBodyConsumed;
|
||||
|
||||
// touched only on the main-thread.
|
||||
bool mShuttingDown;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_BodyConsumer_h
|
@ -2,12 +2,13 @@
|
||||
* 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 "FetchStream.h"
|
||||
#include "BodyStream.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsStreamUtils.h"
|
||||
@ -17,9 +18,47 @@ static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FetchStream::WorkerShutdown final : public WorkerControlRunnable {
|
||||
// BodyStreamHolder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(BodyStreamHolder)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BodyStreamHolder)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BodyStreamHolder)
|
||||
if (tmp->mBodyStream) {
|
||||
tmp->mBodyStream->ReleaseObjects();
|
||||
MOZ_ASSERT(!tmp->mBodyStream);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(BodyStreamHolder)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(BodyStreamHolder)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BodyStreamHolder)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
BodyStreamHolder::BodyStreamHolder() : mBodyStream(nullptr) {}
|
||||
|
||||
void BodyStreamHolder::StoreBodyStream(BodyStream* aBodyStream) {
|
||||
MOZ_ASSERT(aBodyStream);
|
||||
MOZ_ASSERT(!mBodyStream);
|
||||
mBodyStream = aBodyStream;
|
||||
}
|
||||
|
||||
void BodyStreamHolder::ForgetBodyStream() {
|
||||
MOZ_ASSERT_IF(mStreamCreated, mBodyStream);
|
||||
mBodyStream = nullptr;
|
||||
}
|
||||
|
||||
// BodyStream
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class BodyStream::WorkerShutdown final : public WorkerControlRunnable {
|
||||
public:
|
||||
WorkerShutdown(WorkerPrivate* aWorkerPrivate, RefPtr<FetchStream> aStream)
|
||||
WorkerShutdown(WorkerPrivate* aWorkerPrivate, RefPtr<BodyStream> aStream)
|
||||
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mStream(aStream) {}
|
||||
|
||||
@ -37,23 +76,24 @@ class FetchStream::WorkerShutdown final : public WorkerControlRunnable {
|
||||
bool aDispatchResult) override {}
|
||||
|
||||
private:
|
||||
RefPtr<FetchStream> mStream;
|
||||
RefPtr<BodyStream> mStream;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FetchStream, nsIInputStreamCallback, nsIObserver,
|
||||
NS_IMPL_ISUPPORTS(BodyStream, nsIInputStreamCallback, nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
/* static */
|
||||
void FetchStream::Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
|
||||
void BodyStream::Create(JSContext* aCx, BodyStreamHolder* aStreamHolder,
|
||||
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
|
||||
JS::MutableHandle<JSObject*> aStream,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aCx);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
|
||||
|
||||
RefPtr<FetchStream> stream =
|
||||
new FetchStream(aGlobal, aStreamHolder, aInputStream);
|
||||
RefPtr<BodyStream> stream =
|
||||
new BodyStream(aGlobal, aStreamHolder, aInputStream);
|
||||
|
||||
auto cleanup = MakeScopeExit([stream] { stream->Close(); });
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
@ -86,22 +126,29 @@ void FetchStream::Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
|
||||
}
|
||||
|
||||
aRv.MightThrowJSException();
|
||||
JS::Rooted<JSObject*> body(
|
||||
aCx, JS::NewReadableExternalSourceStreamObject(aCx, stream));
|
||||
JS::Rooted<JSObject*> body(aCx, JS::NewReadableExternalSourceStreamObject(
|
||||
aCx, stream, aStreamHolder));
|
||||
if (!body) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will be released in FetchStream::FinalizeCallback(). We are
|
||||
// This will be released in BodyStream::FinalizeCallback(). We are
|
||||
// guaranteed the jsapi will call FinalizeCallback when ReadableStream
|
||||
// js object is finalized.
|
||||
NS_ADDREF(stream.get());
|
||||
|
||||
aStream.set(body);
|
||||
cleanup.release();
|
||||
|
||||
aStreamHolder->StoreBodyStream(stream);
|
||||
aStreamHolder->SetReadableStreamBody(body);
|
||||
|
||||
#ifdef DEBUG
|
||||
aStreamHolder->mStreamCreated = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FetchStream::requestData(JSContext* aCx, JS::HandleObject aStream,
|
||||
void BodyStream::requestData(JSContext* aCx, JS::HandleObject aStream,
|
||||
size_t aDesiredSize) {
|
||||
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
bool disturbed;
|
||||
@ -168,7 +215,7 @@ void FetchStream::requestData(JSContext* aCx, JS::HandleObject aStream,
|
||||
// All good.
|
||||
}
|
||||
|
||||
void FetchStream::writeIntoReadRequestBuffer(JSContext* aCx,
|
||||
void BodyStream::writeIntoReadRequestBuffer(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
void* aBuffer, size_t aLength,
|
||||
size_t* aByteWritten) {
|
||||
@ -207,7 +254,7 @@ void FetchStream::writeIntoReadRequestBuffer(JSContext* aCx,
|
||||
// All good.
|
||||
}
|
||||
|
||||
JS::Value FetchStream::cancel(JSContext* aCx, JS::HandleObject aStream,
|
||||
JS::Value BodyStream::cancel(JSContext* aCx, JS::HandleObject aStream,
|
||||
JS::HandleValue aReason) {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
@ -231,9 +278,9 @@ JS::Value FetchStream::cancel(JSContext* aCx, JS::HandleObject aStream,
|
||||
return JS::UndefinedValue();
|
||||
}
|
||||
|
||||
void FetchStream::onClosed(JSContext* aCx, JS::HandleObject aStream) {}
|
||||
void BodyStream::onClosed(JSContext* aCx, JS::HandleObject aStream) {}
|
||||
|
||||
void FetchStream::onErrored(JSContext* aCx, JS::HandleObject aStream,
|
||||
void BodyStream::onErrored(JSContext* aCx, JS::HandleObject aStream,
|
||||
JS::HandleValue aReason) {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
@ -249,19 +296,19 @@ void FetchStream::onErrored(JSContext* aCx, JS::HandleObject aStream,
|
||||
ReleaseObjects();
|
||||
}
|
||||
|
||||
void FetchStream::finalize() {
|
||||
void BodyStream::finalize() {
|
||||
// This can be called in any thread.
|
||||
|
||||
// This takes ownership of the ref created in FetchStream::Create().
|
||||
RefPtr<FetchStream> stream = dont_AddRef(this);
|
||||
// This takes ownership of the ref created in BodyStream::Create().
|
||||
RefPtr<BodyStream> stream = dont_AddRef(this);
|
||||
|
||||
stream->ReleaseObjects();
|
||||
}
|
||||
|
||||
FetchStream::FetchStream(nsIGlobalObject* aGlobal,
|
||||
FetchStreamHolder* aStreamHolder,
|
||||
BodyStream::BodyStream(nsIGlobalObject* aGlobal,
|
||||
BodyStreamHolder* aStreamHolder,
|
||||
nsIInputStream* aInputStream)
|
||||
: mMutex("FetchStream::mMutex"),
|
||||
: mMutex("BodyStream::mMutex"),
|
||||
mState(eInitializing),
|
||||
mGlobal(aGlobal),
|
||||
mStreamHolder(aStreamHolder),
|
||||
@ -271,9 +318,9 @@ FetchStream::FetchStream(nsIGlobalObject* aGlobal,
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
|
||||
}
|
||||
|
||||
FetchStream::~FetchStream() {}
|
||||
BodyStream::~BodyStream() {}
|
||||
|
||||
void FetchStream::ErrorPropagation(JSContext* aCx,
|
||||
void BodyStream::ErrorPropagation(JSContext* aCx,
|
||||
const MutexAutoLock& aProofOfLock,
|
||||
JS::HandleObject aStream, nsresult aError) {
|
||||
AssertIsOnOwningThread();
|
||||
@ -302,7 +349,7 @@ void FetchStream::ErrorPropagation(JSContext* aCx,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
|
||||
BodyStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStream);
|
||||
|
||||
@ -320,8 +367,13 @@ FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == eReading || mState == eChecking);
|
||||
|
||||
JSObject* streamObj = mStreamHolder->GetReadableStreamBody();
|
||||
if (!streamObj) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSContext* cx = aes.cx();
|
||||
JS::Rooted<JSObject*> stream(cx, mStreamHolder->ReadableStreamBody());
|
||||
JS::Rooted<JSObject*> stream(cx, streamObj);
|
||||
|
||||
uint64_t size = 0;
|
||||
nsresult rv = mInputStream->Available(&size);
|
||||
@ -347,20 +399,21 @@ FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
|
||||
|
||||
lock.reset();
|
||||
|
||||
DebugOnly<bool> ok =
|
||||
JS::ReadableStreamUpdateDataAvailableFromSource(cx, stream, size);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult FetchStream::RetrieveInputStream(
|
||||
nsresult BodyStream::RetrieveInputStream(
|
||||
JS::ReadableStreamUnderlyingSource* aUnderlyingReadableStreamSource,
|
||||
nsIInputStream** aInputStream) {
|
||||
MOZ_ASSERT(aUnderlyingReadableStreamSource);
|
||||
MOZ_ASSERT(aInputStream);
|
||||
|
||||
RefPtr<FetchStream> stream =
|
||||
static_cast<FetchStream*>(aUnderlyingReadableStreamSource);
|
||||
RefPtr<BodyStream> stream =
|
||||
static_cast<BodyStream*>(aUnderlyingReadableStreamSource);
|
||||
stream->AssertIsOnOwningThread();
|
||||
|
||||
// if mOriginalInputStream is null, the reading already started. We don't want
|
||||
@ -374,7 +427,7 @@ nsresult FetchStream::RetrieveInputStream(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void FetchStream::Close() {
|
||||
void BodyStream::Close() {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
@ -389,12 +442,17 @@ void FetchStream::Close() {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* streamObj = mStreamHolder->GetReadableStreamBody();
|
||||
if (streamObj) {
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> stream(cx, mStreamHolder->ReadableStreamBody());
|
||||
JS::Rooted<JSObject*> stream(cx, streamObj);
|
||||
CloseAndReleaseObjects(cx, lock, stream);
|
||||
} else {
|
||||
ReleaseObjects(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void FetchStream::CloseAndReleaseObjects(JSContext* aCx,
|
||||
void BodyStream::CloseAndReleaseObjects(JSContext* aCx,
|
||||
const MutexAutoLock& aProofOfLock,
|
||||
JS::HandleObject aStream) {
|
||||
AssertIsOnOwningThread();
|
||||
@ -412,12 +470,12 @@ void FetchStream::CloseAndReleaseObjects(JSContext* aCx,
|
||||
}
|
||||
}
|
||||
|
||||
void FetchStream::ReleaseObjects() {
|
||||
void BodyStream::ReleaseObjects() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
ReleaseObjects(lock);
|
||||
}
|
||||
|
||||
void FetchStream::ReleaseObjects(const MutexAutoLock& aProofOfLock) {
|
||||
void BodyStream::ReleaseObjects(const MutexAutoLock& aProofOfLock) {
|
||||
// This method can be called on 2 possible threads: the owning one and a JS
|
||||
// thread used to release resources. If we are on the JS thread, we need to
|
||||
// dispatch a runnable to go back to the owning thread in order to release
|
||||
@ -428,8 +486,6 @@ void FetchStream::ReleaseObjects(const MutexAutoLock& aProofOfLock) {
|
||||
return;
|
||||
}
|
||||
|
||||
mState = eClosed;
|
||||
|
||||
if (!NS_IsMainThread() && !IsCurrentThreadRunningWorker()) {
|
||||
// Let's dispatch a WorkerControlRunnable if the owning thread is a worker.
|
||||
if (mWorkerRef) {
|
||||
@ -440,15 +496,17 @@ void FetchStream::ReleaseObjects(const MutexAutoLock& aProofOfLock) {
|
||||
}
|
||||
|
||||
// A normal runnable of the owning thread is the main-thread.
|
||||
RefPtr<FetchStream> self = this;
|
||||
RefPtr<BodyStream> self = this;
|
||||
RefPtr<Runnable> r = NS_NewRunnableFunction(
|
||||
"FetchStream::ReleaseObjects", [self]() { self->ReleaseObjects(); });
|
||||
"BodyStream::ReleaseObjects", [self]() { self->ReleaseObjects(); });
|
||||
mOwningEventTarget->Dispatch(r.forget());
|
||||
return;
|
||||
}
|
||||
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
mState = eClosed;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
@ -456,16 +514,23 @@ void FetchStream::ReleaseObjects(const MutexAutoLock& aProofOfLock) {
|
||||
}
|
||||
}
|
||||
|
||||
JSObject* streamObj = mStreamHolder->GetReadableStreamBody();
|
||||
if (streamObj) {
|
||||
// Let's inform the JSEngine that we are going to be released.
|
||||
JS::ReadableStreamReleaseCCObject(streamObj);
|
||||
}
|
||||
|
||||
mWorkerRef = nullptr;
|
||||
mGlobal = nullptr;
|
||||
|
||||
mStreamHolder->ForgetBodyStream();
|
||||
mStreamHolder->NullifyStream();
|
||||
mStreamHolder = nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void FetchStream::AssertIsOnOwningThread() {
|
||||
NS_ASSERT_OWNINGTHREAD(FetchStream);
|
||||
void BodyStream::AssertIsOnOwningThread() {
|
||||
NS_ASSERT_OWNINGTHREAD(BodyStream);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -473,7 +538,7 @@ void FetchStream::AssertIsOnOwningThread() {
|
||||
// -----------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchStream::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
BodyStream::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
AssertIsOnMainThread();
|
||||
AssertIsOnOwningThread();
|
@ -2,10 +2,9 @@
|
||||
* 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_FetchStream_h
|
||||
#define mozilla_dom_FetchStream_h
|
||||
#ifndef mozilla_dom_BodyStream_h
|
||||
#define mozilla_dom_BodyStream_h
|
||||
|
||||
#include "Fetch.h"
|
||||
#include "jsapi.h"
|
||||
#include "js/Stream.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
@ -20,21 +19,58 @@ class nsIInputStream;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FetchStreamHolder;
|
||||
class BodyStream;
|
||||
class WeakWorkerRef;
|
||||
|
||||
class FetchStream final : public nsIInputStreamCallback,
|
||||
class BodyStreamHolder : public nsISupports {
|
||||
friend class BodyStream;
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(BodyStreamHolder)
|
||||
|
||||
BodyStreamHolder();
|
||||
|
||||
virtual void NullifyStream() = 0;
|
||||
|
||||
virtual void MarkAsRead() = 0;
|
||||
|
||||
virtual void SetReadableStreamBody(JSObject* aBody) = 0;
|
||||
|
||||
virtual JSObject* GetReadableStreamBody() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~BodyStreamHolder() = default;
|
||||
|
||||
private:
|
||||
void StoreBodyStream(BodyStream* aBodyStream);
|
||||
void ForgetBodyStream();
|
||||
|
||||
// Raw pointer because BodyStream keeps BodyStreamHolder alive and it
|
||||
// nullifies this stream before being released.
|
||||
BodyStream* mBodyStream;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mStreamCreated = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
class BodyStream final : public nsIInputStreamCallback,
|
||||
public nsIObserver,
|
||||
public nsSupportsWeakReference,
|
||||
private JS::ReadableStreamUnderlyingSource {
|
||||
friend class BodyStreamHolder;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static void Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
|
||||
// This method creates a JS ReadableStream object and it assigns it to the
|
||||
// aStreamHolder calling SetReadableStreamBody().
|
||||
static void Create(JSContext* aCx, BodyStreamHolder* aStreamHolder,
|
||||
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
|
||||
JS::MutableHandle<JSObject*> aStream, ErrorResult& aRv);
|
||||
ErrorResult& aRv);
|
||||
|
||||
void Close();
|
||||
|
||||
@ -43,9 +79,9 @@ class FetchStream final : public nsIInputStreamCallback,
|
||||
nsIInputStream** aInputStream);
|
||||
|
||||
private:
|
||||
FetchStream(nsIGlobalObject* aGlobal, FetchStreamHolder* aStreamHolder,
|
||||
BodyStream(nsIGlobalObject* aGlobal, BodyStreamHolder* aStreamHolder,
|
||||
nsIInputStream* aInputStream);
|
||||
~FetchStream();
|
||||
~BodyStream();
|
||||
|
||||
#ifdef DEBUG
|
||||
void AssertIsOnOwningThread();
|
||||
@ -107,7 +143,7 @@ class FetchStream final : public nsIInputStreamCallback,
|
||||
eClosed,
|
||||
};
|
||||
|
||||
// We need a mutex because JS engine can release FetchStream on a non-owning
|
||||
// We need a mutex because JS engine can release BodyStream on a non-owning
|
||||
// thread. We must be sure that the releasing of resources doesn't trigger
|
||||
// race conditions.
|
||||
Mutex mMutex;
|
||||
@ -116,7 +152,7 @@ class FetchStream final : public nsIInputStreamCallback,
|
||||
State mState;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
RefPtr<FetchStreamHolder> mStreamHolder;
|
||||
RefPtr<BodyStreamHolder> mStreamHolder;
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
|
||||
// This is the original inputStream received during the CTOR. It will be
|
||||
@ -131,4 +167,4 @@ class FetchStream final : public nsIInputStreamCallback,
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FetchStream_h
|
||||
#endif // mozilla_dom_BodyStream_h
|
@ -13,18 +13,18 @@
|
||||
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/BindContext.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "mozilla/InternalMutationEvent.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "nsChangeHint.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "mozilla/dom/DirectionalityUtils.h"
|
||||
#include "nsBindingManager.h"
|
||||
#include "nsCCUncollectableMarker.h"
|
||||
@ -93,12 +93,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CharacterData)
|
||||
nsIContent::Unlink(tmp);
|
||||
|
||||
// Clear flag here because unlinking slots will clear the
|
||||
// containing shadow root pointer.
|
||||
tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
|
||||
nsContentSlots* slots = tmp->GetExistingContentSlots();
|
||||
if (slots) {
|
||||
if (nsContentSlots* slots = tmp->GetExistingContentSlots()) {
|
||||
slots->Unlink();
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
@ -243,13 +238,14 @@ nsresult CharacterData::SetTextInternal(
|
||||
if (aNotify) {
|
||||
CharacterDataChangeInfo info = {aOffset == textLength, aOffset, endOffset,
|
||||
aLength, aDetails};
|
||||
nsNodeUtils::CharacterDataWillChange(this, info);
|
||||
MutationObservers::NotifyCharacterDataWillChange(this, info);
|
||||
}
|
||||
|
||||
Directionality oldDir = eDir_NotSet;
|
||||
bool dirAffectsAncestor =
|
||||
(NodeType() == TEXT_NODE &&
|
||||
TextNodeWillChangeDirection(this, &oldDir, aOffset));
|
||||
TextNodeWillChangeDirection(static_cast<nsTextNode*>(this), &oldDir,
|
||||
aOffset));
|
||||
|
||||
if (aOffset == 0 && endOffset == textLength) {
|
||||
// Replacing whole text or old text was empty. Don't bother to check for
|
||||
@ -321,7 +317,7 @@ nsresult CharacterData::SetTextInternal(
|
||||
if (aNotify) {
|
||||
CharacterDataChangeInfo info = {aOffset == textLength, aOffset, endOffset,
|
||||
aLength, aDetails};
|
||||
nsNodeUtils::CharacterDataChanged(this, info);
|
||||
MutationObservers::NotifyCharacterDataChanged(this, info);
|
||||
|
||||
if (haveMutationListeners) {
|
||||
InternalMutationEvent mutation(true, eLegacyCharacterDataModified);
|
||||
@ -388,121 +384,133 @@ void CharacterData::ToCString(nsAString& aBuf, int32_t aOffset,
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult CharacterData::BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent) {
|
||||
MOZ_ASSERT(aParent || aDocument, "Must have document if no parent!");
|
||||
MOZ_ASSERT(NODE_FROM(aParent, aDocument)->OwnerDoc() == OwnerDoc(),
|
||||
nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||
MOZ_ASSERT(aParent.IsContent() || aParent.IsDocument(),
|
||||
"Must have content or document parent!");
|
||||
MOZ_ASSERT(aParent.OwnerDoc() == OwnerDoc(),
|
||||
"Must have the same owner document");
|
||||
MOZ_ASSERT(!aParent || aDocument == aParent->GetUncomposedDoc(),
|
||||
"aDocument must be current doc of aParent");
|
||||
MOZ_ASSERT(!GetUncomposedDoc() && !IsInUncomposedDoc(),
|
||||
"Already have a document. Unbind first!");
|
||||
MOZ_ASSERT(OwnerDoc() == &aContext.OwnerDoc(), "These should match too");
|
||||
MOZ_ASSERT(!IsInUncomposedDoc(), "Already have a document. Unbind first!");
|
||||
MOZ_ASSERT(!IsInComposedDoc(), "Already have a document. Unbind first!");
|
||||
// Note that as we recurse into the kids, they'll have a non-null parent. So
|
||||
// only assert if our parent is _changing_ while we have a parent.
|
||||
MOZ_ASSERT(!GetParent() || aParent == GetParent(),
|
||||
MOZ_ASSERT(!GetParentNode() || &aParent == GetParentNode(),
|
||||
"Already have a parent. Unbind first!");
|
||||
MOZ_ASSERT(!GetBindingParent() || aBindingParent == GetBindingParent() ||
|
||||
(!aBindingParent && aParent &&
|
||||
aParent->GetBindingParent() == GetBindingParent()),
|
||||
MOZ_ASSERT(
|
||||
!GetBindingParent() ||
|
||||
aContext.GetBindingParent() == GetBindingParent() ||
|
||||
(!aContext.GetBindingParent() && aParent.IsContent() &&
|
||||
aParent.AsContent()->GetBindingParent() == GetBindingParent()),
|
||||
"Already have a binding parent. Unbind first!");
|
||||
MOZ_ASSERT(aBindingParent != this,
|
||||
"Content must not be its own binding parent");
|
||||
MOZ_ASSERT(!IsRootOfNativeAnonymousSubtree() || aBindingParent == aParent,
|
||||
MOZ_ASSERT(!IsRootOfNativeAnonymousSubtree() ||
|
||||
aContext.GetBindingParent() == &aParent,
|
||||
"Native anonymous content must have its parent as its "
|
||||
"own binding parent");
|
||||
|
||||
if (!aBindingParent && aParent) {
|
||||
aBindingParent = aParent->GetBindingParent();
|
||||
}
|
||||
MOZ_ASSERT(aContext.GetBindingParent() || !aParent.IsContent() ||
|
||||
aContext.GetBindingParent() ==
|
||||
aParent.AsContent()->GetBindingParent(),
|
||||
"We should be passed the right binding parent");
|
||||
|
||||
// First set the binding parent
|
||||
if (aBindingParent) {
|
||||
NS_ASSERTION(IsRootOfNativeAnonymousSubtree() ||
|
||||
if (Element* bindingParent = aContext.GetBindingParent()) {
|
||||
ExtendedContentSlots()->mBindingParent = bindingParent;
|
||||
}
|
||||
|
||||
const bool hadParent = !!GetParentNode();
|
||||
|
||||
NS_ASSERTION(!aContext.GetBindingParent() ||
|
||||
IsRootOfNativeAnonymousSubtree() ||
|
||||
!HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) ||
|
||||
(aParent && aParent->IsInNativeAnonymousSubtree()),
|
||||
aParent.IsInNativeAnonymousSubtree(),
|
||||
"Trying to re-bind content from native anonymous subtree to "
|
||||
"non-native anonymous parent!");
|
||||
ExtendedContentSlots()->mBindingParent =
|
||||
aBindingParent; // Weak, so no addref happens.
|
||||
if (aParent->IsInNativeAnonymousSubtree()) {
|
||||
if (aParent.IsInNativeAnonymousSubtree()) {
|
||||
SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
|
||||
}
|
||||
if (aParent->HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
|
||||
if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
|
||||
SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
|
||||
}
|
||||
if (HasFlag(NODE_IS_ANONYMOUS_ROOT)) {
|
||||
aParent->SetMayHaveAnonymousChildren();
|
||||
aParent.SetMayHaveAnonymousChildren();
|
||||
}
|
||||
}
|
||||
|
||||
if (aParent && aParent->IsInShadowTree()) {
|
||||
ClearSubtreeRootPointer();
|
||||
SetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
SetIsConnected(aParent->IsInComposedDoc());
|
||||
MOZ_ASSERT(aParent->GetContainingShadow());
|
||||
ExtendedContentSlots()->mContainingShadow = aParent->GetContainingShadow();
|
||||
}
|
||||
|
||||
bool hadParent = !!GetParentNode();
|
||||
|
||||
// Set parent
|
||||
if (aParent) {
|
||||
if (!GetParent()) {
|
||||
NS_ADDREF(aParent);
|
||||
mParent = &aParent;
|
||||
if (!hadParent && aParent.IsContent()) {
|
||||
SetParentIsContent(true);
|
||||
NS_ADDREF(mParent);
|
||||
}
|
||||
mParent = aParent;
|
||||
} else {
|
||||
mParent = aDocument;
|
||||
}
|
||||
SetParentIsContent(aParent);
|
||||
MOZ_ASSERT(!!GetParent() == aParent.IsContent());
|
||||
|
||||
// XXXbz sXBL/XBL2 issue!
|
||||
|
||||
// Set document
|
||||
if (aDocument) {
|
||||
if (aParent.IsInUncomposedDoc() || aParent.IsInShadowTree()) {
|
||||
// We no longer need to track the subtree pointer (and in fact we'll assert
|
||||
// if we do this any later).
|
||||
ClearSubtreeRootPointer();
|
||||
SetIsConnected(aParent.IsInComposedDoc());
|
||||
|
||||
// XXX See the comment in Element::BindToTree
|
||||
if (aParent.IsInUncomposedDoc()) {
|
||||
SetIsInDocument();
|
||||
SetIsConnected(true);
|
||||
if (mText.IsBidi()) {
|
||||
aDocument->SetBidiEnabled();
|
||||
} else {
|
||||
SetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
MOZ_ASSERT(aParent.IsContent() &&
|
||||
aParent.AsContent()->GetContainingShadow());
|
||||
ExtendedContentSlots()->mContainingShadow =
|
||||
aParent.AsContent()->GetContainingShadow();
|
||||
}
|
||||
|
||||
if (IsInComposedDoc()) {
|
||||
if (mText.IsBidi()) {
|
||||
aContext.OwnerDoc().SetBidiEnabled();
|
||||
}
|
||||
if (aContext.CollectingDisplayedNodeDataDuringLoad()) {
|
||||
aContext.OwnerDoc().AddToVisibleContentHeuristic(mText.GetLength());
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the lazy frame construction bits.
|
||||
UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
|
||||
} else if (!IsInShadowTree()) {
|
||||
} else {
|
||||
// If we're not in the doc and not in a shadow tree,
|
||||
// update our subtree pointer.
|
||||
SetSubtreeRootPointer(aParent->SubtreeRoot());
|
||||
SetSubtreeRootPointer(aParent.SubtreeRoot());
|
||||
}
|
||||
|
||||
nsNodeUtils::ParentChainChanged(this);
|
||||
MutationObservers::NotifyParentChainChanged(this);
|
||||
if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
|
||||
nsNodeUtils::NativeAnonymousChildListChange(this, false);
|
||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, false);
|
||||
}
|
||||
|
||||
UpdateEditableState(false);
|
||||
|
||||
MOZ_ASSERT(aDocument == GetUncomposedDoc(), "Bound to wrong document");
|
||||
MOZ_ASSERT(aParent == GetParent(), "Bound to wrong parent");
|
||||
MOZ_ASSERT(aBindingParent == GetBindingParent(),
|
||||
"Bound to wrong binding parent");
|
||||
// Ensure we only do these once, in the case we move the shadow host around.
|
||||
if (aContext.SubtreeRootChanges()) {
|
||||
HandleShadowDOMRelatedInsertionSteps(hadParent);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(OwnerDoc() == aParent.OwnerDoc(), "Bound to wrong document");
|
||||
MOZ_ASSERT(IsInComposedDoc() == aContext.InComposedDoc());
|
||||
MOZ_ASSERT(IsInUncomposedDoc() == aContext.InUncomposedDoc());
|
||||
MOZ_ASSERT(&aParent == GetParentNode(), "Bound to wrong parent node");
|
||||
MOZ_ASSERT(aContext.GetBindingParent() == GetBindingParent(),
|
||||
"Bound to wrong binding parent");
|
||||
MOZ_ASSERT(aParent.IsInUncomposedDoc() == IsInUncomposedDoc());
|
||||
MOZ_ASSERT(aParent.IsInComposedDoc() == IsInComposedDoc());
|
||||
MOZ_ASSERT(aParent.IsInShadowTree() == IsInShadowTree());
|
||||
MOZ_ASSERT(aParent.SubtreeRoot() == SubtreeRoot());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void CharacterData::UnbindFromTree(bool aDeep, bool aNullParent) {
|
||||
void CharacterData::UnbindFromTree(bool aNullParent) {
|
||||
// Unset frame flags; if we need them again later, they'll get set again.
|
||||
UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | NS_REFRAME_IF_WHITESPACE);
|
||||
|
||||
HandleShadowDOMRelatedRemovalSteps(aNullParent);
|
||||
|
||||
Document* document = GetComposedDoc();
|
||||
|
||||
if (aNullParent) {
|
||||
if (this->IsRootOfNativeAnonymousSubtree()) {
|
||||
nsNodeUtils::NativeAnonymousChildListChange(this, true);
|
||||
if (IsRootOfNativeAnonymousSubtree()) {
|
||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, true);
|
||||
}
|
||||
if (GetParent()) {
|
||||
NS_RELEASE(mParent);
|
||||
@ -540,7 +548,7 @@ void CharacterData::UnbindFromTree(bool aDeep, bool aNullParent) {
|
||||
}
|
||||
}
|
||||
|
||||
nsNodeUtils::ParentChainChanged(this);
|
||||
MutationObservers::NotifyParentChainChanged(this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -60,10 +60,14 @@ enum {
|
||||
// This bit is set if the node may be modified frequently. This is typically
|
||||
// specified if the instance is in <input> or <textarea>.
|
||||
NS_MAYBE_MODIFIED_FREQUENTLY = CHARACTER_DATA_FLAG_BIT(6),
|
||||
|
||||
// This bit is set if the node may be masked because of being in a password
|
||||
// field.
|
||||
NS_MAYBE_MASKED = CHARACTER_DATA_FLAG_BIT(7),
|
||||
};
|
||||
|
||||
// Make sure we have enough space for those bits
|
||||
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 7);
|
||||
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 8);
|
||||
|
||||
#undef CHARACTER_DATA_FLAG_BIT
|
||||
|
||||
@ -85,6 +89,7 @@ class CharacterData : public nsIContent {
|
||||
void MarkAsMaybeModifiedFrequently() {
|
||||
SetFlags(NS_MAYBE_MODIFIED_FREQUENTLY);
|
||||
}
|
||||
void MarkAsMaybeMasked() { SetFlags(NS_MAYBE_MASKED); }
|
||||
|
||||
NS_IMPL_FROMNODE_HELPER(CharacterData, IsCharacterData())
|
||||
|
||||
@ -105,20 +110,19 @@ class CharacterData : public nsIContent {
|
||||
}
|
||||
|
||||
// Implementation for nsIContent
|
||||
nsresult BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent) override;
|
||||
nsresult BindToTree(BindContext&, nsINode& aParent) override;
|
||||
|
||||
void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override;
|
||||
void UnbindFromTree(bool aNullParent = true) override;
|
||||
|
||||
already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) final {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const nsTextFragment* GetText() override { return &mText; }
|
||||
uint32_t TextLength() const final { return TextDataLength(); }
|
||||
|
||||
const nsTextFragment& TextFragment() const { return mText; }
|
||||
|
||||
uint32_t TextLength() const final { return TextDataLength(); }
|
||||
uint32_t TextDataLength() const { return mText.GetLength(); }
|
||||
|
||||
/**
|
||||
* Set the text to the given value. If aNotify is true then
|
||||
@ -195,8 +199,6 @@ class CharacterData : public nsIContent {
|
||||
void ReplaceData(uint32_t aOffset, uint32_t aCount, const nsAString& aData,
|
||||
ErrorResult& rv);
|
||||
|
||||
uint32_t TextDataLength() const { return mText.GetLength(); }
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -9,7 +9,7 @@ using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
already_AddRefed<ChromeNodeList> ChromeNodeList::Constructor(
|
||||
const GlobalObject& aGlobal, ErrorResult& aRv) {
|
||||
const GlobalObject& aGlobal) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
Document* root = win ? win->GetExtantDoc() : nullptr;
|
||||
RefPtr<ChromeNodeList> list = new ChromeNodeList(root);
|
||||
|
@ -16,7 +16,7 @@ class ChromeNodeList final : public nsSimpleContentList {
|
||||
explicit ChromeNodeList(nsINode* aOwner) : nsSimpleContentList(aOwner) {}
|
||||
|
||||
static already_AddRefed<ChromeNodeList> Constructor(
|
||||
const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
const GlobalObject& aGlobal);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
@ -206,17 +206,12 @@ ContentAreaDropListener.prototype =
|
||||
if (sourceNode &&
|
||||
(sourceNode.localName !== "browser" ||
|
||||
sourceNode.namespaceURI !== "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")) {
|
||||
// Use sourceNode's principal only if the sourceNode is not browser.
|
||||
// Use sourceNode's csp only if the sourceNode is not browser.
|
||||
//
|
||||
// If sourceNode is browser, the actual triggering principal may be
|
||||
// differ than sourceNode's principal, since sourceNode's principal is
|
||||
// top level document's one and the drag may be triggered from a frame
|
||||
// with different principal.
|
||||
if (sourceNode.nodePrincipal) {
|
||||
// Currently we query the CSP from the nodePrincipal. After Bug 965637 we can
|
||||
// query the CSP directly from the sourceNode.
|
||||
return sourceNode.nodePrincipal.csp;
|
||||
}
|
||||
// If sourceNode is browser, the actual triggering csp may be differ than sourceNode's csp,
|
||||
// since sourceNode's csp is top level document's one and the drag may be triggered from a
|
||||
// frame with different csp.
|
||||
return sourceNode.csp;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
@ -4,19 +4,23 @@
|
||||
|
||||
#include "ContentBlockingLog.h"
|
||||
|
||||
#include "nsStringStream.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/RandomNum.h"
|
||||
#include "mozilla/StaticPrefs_privacy.h"
|
||||
#include "mozilla/StaticPrefs_telemetry.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/XorShift128PlusRNG.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static LazyLogModule gContentBlockingLog("ContentBlockingLog");
|
||||
#define LOG(fmt, ...) \
|
||||
MOZ_LOG(gContentBlockingLog, mozilla::LogLevel::Debug, (fmt, ##__VA_ARGS__))
|
||||
MOZ_LOG(gContentBlockingLog, LogLevel::Debug, (fmt, ##__VA_ARGS__))
|
||||
|
||||
typedef mozilla::Telemetry::OriginMetricID OriginMetricID;
|
||||
typedef Telemetry::OriginMetricID OriginMetricID;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// randomly choose 1% users included in the content blocking measurement
|
||||
@ -64,7 +68,7 @@ static bool IsReportingPerUserEnabled() {
|
||||
|
||||
static bool IsReportingPerDocumentEnabled() {
|
||||
constexpr double boundary =
|
||||
kRatioReportDocument * std::numeric_limits<uint64_t>::max();
|
||||
kRatioReportDocument * double(std::numeric_limits<uint64_t>::max());
|
||||
Maybe<uint64_t> randomNum = RandomUint64();
|
||||
return randomNum.isSome() && randomNum.value() <= boundary;
|
||||
}
|
||||
|
@ -8,9 +8,10 @@
|
||||
#include "mozilla/AntiTrackingCommon.h"
|
||||
#include "mozilla/JSONWriter.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/StaticPrefs_browser.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWindowSizes.h"
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/RangeBoundary.h"
|
||||
#include "mozilla/RangeUtils.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsElementTable.h"
|
||||
@ -115,7 +116,7 @@ nsresult ContentIteratorBase::Init(nsINode* aStartContainer,
|
||||
uint32_t aEndOffset) {
|
||||
mIsDone = false;
|
||||
|
||||
if (NS_WARN_IF(!nsRange::IsValidPoints(aStartContainer, aStartOffset,
|
||||
if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStartContainer, aStartOffset,
|
||||
aEndContainer, aEndOffset))) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
@ -128,8 +129,7 @@ nsresult ContentIteratorBase::Init(const RawRangeBoundary& aStart,
|
||||
const RawRangeBoundary& aEnd) {
|
||||
mIsDone = false;
|
||||
|
||||
if (NS_WARN_IF(!nsRange::IsValidPoints(aStart.Container(), aStart.Offset(),
|
||||
aEnd.Container(), aEnd.Offset()))) {
|
||||
if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStart, aEnd))) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@ -260,7 +260,7 @@ nsresult ContentIteratorBase::InitInternal(const RawRangeBoundary& aStart,
|
||||
NS_WARNING_ASSERTION(mLast, "PrevNode returned null");
|
||||
if (mLast && mLast != mFirst &&
|
||||
NS_WARN_IF(!NodeIsInTraversalRange(
|
||||
mLast, mPre, RawRangeBoundary(mFirst, 0), aEnd))) {
|
||||
mLast, mPre, RawRangeBoundary(mFirst, 0u), aEnd))) {
|
||||
mLast = nullptr;
|
||||
}
|
||||
} else {
|
||||
@ -556,31 +556,30 @@ nsresult ContentIteratorBase::PositionAt(nsINode* aCurNode) {
|
||||
|
||||
// Check to see if the node falls within the traversal range.
|
||||
|
||||
RawRangeBoundary first(mFirst, 0);
|
||||
RawRangeBoundary last(mLast, 0);
|
||||
RawRangeBoundary first(mFirst, 0u);
|
||||
RawRangeBoundary last(mLast, 0u);
|
||||
|
||||
if (mFirst && mLast) {
|
||||
if (mPre) {
|
||||
// In pre we want to record the point immediately before mFirst, which is
|
||||
// the point immediately after mFirst's previous sibling.
|
||||
first.SetAfterRef(mFirst->GetParentNode(), mFirst->GetPreviousSibling());
|
||||
first = {mFirst->GetParentNode(), mFirst->GetPreviousSibling()};
|
||||
|
||||
// If mLast has no children, then we want to make sure to include it.
|
||||
if (!mLast->HasChildren()) {
|
||||
last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
|
||||
last = {mLast->GetParentNode(), mLast->AsContent()};
|
||||
}
|
||||
} else {
|
||||
// If the first node has any children, we want to be immediately after the
|
||||
// last. Otherwise we want to be immediately before mFirst.
|
||||
if (mFirst->HasChildren()) {
|
||||
first.SetAfterRef(mFirst, mFirst->GetLastChild());
|
||||
first = {mFirst, mFirst->GetLastChild()};
|
||||
} else {
|
||||
first.SetAfterRef(mFirst->GetParentNode(),
|
||||
mFirst->GetPreviousSibling());
|
||||
first = {mFirst->GetParentNode(), mFirst->GetPreviousSibling()};
|
||||
}
|
||||
|
||||
// Set the last point immediately after the final node.
|
||||
last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
|
||||
last = {mLast->GetParentNode(), mLast->AsContent()};
|
||||
}
|
||||
}
|
||||
|
||||
@ -641,19 +640,18 @@ nsresult ContentSubtreeIterator::Init(nsINode* aStartContainer,
|
||||
RawRangeBoundary(aEndContainer, aEndOffset));
|
||||
}
|
||||
|
||||
nsresult ContentSubtreeIterator::Init(const RawRangeBoundary& aStart,
|
||||
const RawRangeBoundary& aEnd) {
|
||||
nsresult ContentSubtreeIterator::Init(const RawRangeBoundary& aStartBoundary,
|
||||
const RawRangeBoundary& aEndBoundary) {
|
||||
mIsDone = false;
|
||||
|
||||
RefPtr<nsRange> range;
|
||||
nsresult rv = nsRange::CreateRange(aStart, aEnd, getter_AddRefs(range));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!range) ||
|
||||
NS_WARN_IF(!range->IsPositioned())) {
|
||||
RefPtr<nsRange> range =
|
||||
nsRange::Create(aStartBoundary, aEndBoundary, IgnoreErrors());
|
||||
if (NS_WARN_IF(!range) || NS_WARN_IF(!range->IsPositioned())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(range->StartRef() != aStart) ||
|
||||
NS_WARN_IF(range->EndRef() != aEnd)) {
|
||||
if (NS_WARN_IF(range->StartRef() != aStartBoundary) ||
|
||||
NS_WARN_IF(range->EndRef() != aEndBoundary)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -734,7 +732,7 @@ nsresult ContentSubtreeIterator::InitWithRange() {
|
||||
// we have a range that does not fully contain any node.
|
||||
|
||||
bool nodeBefore, nodeAfter;
|
||||
MOZ_ALWAYS_SUCCEEDS(nsRange::CompareNodeToRange(firstCandidate, mRange,
|
||||
MOZ_ALWAYS_SUCCEEDS(RangeUtils::CompareNodeToRange(firstCandidate, mRange,
|
||||
&nodeBefore, &nodeAfter));
|
||||
|
||||
if (nodeBefore || nodeAfter) {
|
||||
@ -779,7 +777,7 @@ nsresult ContentSubtreeIterator::InitWithRange() {
|
||||
// confirm that this last possible contained node is indeed contained. Else
|
||||
// we have a range that does not fully contain any node.
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(nsRange::CompareNodeToRange(lastCandidate, mRange,
|
||||
MOZ_ALWAYS_SUCCEEDS(RangeUtils::CompareNodeToRange(lastCandidate, mRange,
|
||||
&nodeBefore, &nodeAfter));
|
||||
|
||||
if (nodeBefore || nodeAfter) {
|
||||
@ -899,7 +897,7 @@ nsIContent* ContentSubtreeIterator::GetTopAncestorInRange(nsINode* aNode) {
|
||||
// sanity check: aNode is itself in the range
|
||||
bool nodeBefore, nodeAfter;
|
||||
nsresult res =
|
||||
nsRange::CompareNodeToRange(aNode, mRange, &nodeBefore, &nodeAfter);
|
||||
RangeUtils::CompareNodeToRange(aNode, mRange, &nodeBefore, &nodeAfter);
|
||||
NS_ASSERTION(NS_SUCCEEDED(res) && !nodeBefore && !nodeAfter,
|
||||
"aNode isn't in mRange, or something else weird happened");
|
||||
if (NS_FAILED(res) || nodeBefore || nodeAfter) {
|
||||
@ -917,8 +915,8 @@ nsIContent* ContentSubtreeIterator::GetTopAncestorInRange(nsINode* aNode) {
|
||||
if (!parent || !parent->GetParentNode()) {
|
||||
return content;
|
||||
}
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
nsRange::CompareNodeToRange(parent, mRange, &nodeBefore, &nodeAfter));
|
||||
MOZ_ALWAYS_SUCCEEDS(RangeUtils::CompareNodeToRange(
|
||||
parent, mRange, &nodeBefore, &nodeAfter));
|
||||
|
||||
if (nodeBefore || nodeAfter) {
|
||||
return content;
|
||||
|
@ -176,8 +176,8 @@ class ContentSubtreeIterator final : public ContentIteratorBase {
|
||||
virtual nsresult Init(nsRange* aRange) override;
|
||||
virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
|
||||
nsINode* aEndContainer, uint32_t aEndOffset) override;
|
||||
virtual nsresult Init(const RawRangeBoundary& aStart,
|
||||
const RawRangeBoundary& aEnd) override;
|
||||
virtual nsresult Init(const RawRangeBoundary& aStartBoundary,
|
||||
const RawRangeBoundary& aEndBoundary) override;
|
||||
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include "nsFrameMessageManager.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
@ -42,13 +42,6 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
||||
ErrorResult& aRv) {
|
||||
JS::Rooted<JSObject*> view(aCx, aArray.Obj());
|
||||
|
||||
if (JS_IsTypedArrayObject(view) && JS_GetTypedArraySharedness(view)) {
|
||||
// Throw if the object is mapping shared memory (must opt in).
|
||||
aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(
|
||||
NS_LITERAL_STRING("Argument of Crypto.getRandomValues"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Throw if the wrong type of ArrayBufferView is passed in
|
||||
// (Part of the Web Crypto API spec)
|
||||
switch (JS_GetArrayBufferViewType(view)) {
|
||||
|
@ -172,6 +172,12 @@ void CustomElementData::SetCustomElementDefinition(
|
||||
mCustomElementDefinition = aDefinition;
|
||||
}
|
||||
|
||||
void CustomElementData::AttachedInternals() {
|
||||
MOZ_ASSERT(!mIsAttachedInternals);
|
||||
|
||||
mIsAttachedInternals = true;
|
||||
}
|
||||
|
||||
CustomElementDefinition* CustomElementData::GetCustomElementDefinition() {
|
||||
MOZ_ASSERT(mCustomElementDefinition ? mState == State::eCustom
|
||||
: mState != State::eCustom);
|
||||
@ -634,7 +640,62 @@ int32_t CustomElementRegistry::InferNamespace(
|
||||
return kNameSpaceID_XHTML;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#element-definition
|
||||
bool CustomElementRegistry::JSObjectToAtomArray(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aConstructor, const char16_t* aName,
|
||||
nsTArray<RefPtr<nsAtom>>& aArray, ErrorResult& aRv) {
|
||||
JS::RootedValue iterable(aCx, JS::UndefinedValue());
|
||||
if (!JS_GetUCProperty(aCx, aConstructor, aName,
|
||||
std::char_traits<char16_t>::length(aName), &iterable)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!iterable.isUndefined()) {
|
||||
if (!iterable.isObject()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(nsDependentString(aName));
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::ForOfIterator iter(aCx);
|
||||
if (!iter.init(iterable, JS::ForOfIterator::AllowNonIterable)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!iter.valueIsIterable()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(nsDependentString(aName));
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> attribute(aCx);
|
||||
while (true) {
|
||||
bool done;
|
||||
if (!iter.next(&attribute, &done)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return false;
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsAutoString attrStr;
|
||||
if (!ConvertJSValueToString(aCx, attribute, eStringify, eStringify,
|
||||
attrStr)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aArray.AppendElement(NS_Atomize(attrStr))) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/commit-snapshots/b48bb2238269d90ea4f455a52cdf29505aff3df0/#dom-customelementregistry-define
|
||||
void CustomElementRegistry::Define(
|
||||
JSContext* aCx, const nsAString& aName,
|
||||
CustomElementConstructor& aFunctionConstructor,
|
||||
@ -675,7 +736,10 @@ void CustomElementRegistry::Define(
|
||||
Document* doc = mWindow->GetExtantDoc();
|
||||
RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
|
||||
if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_SYNTAX_ERR,
|
||||
nsPrintfCString("'%s' is not a valid custom element name",
|
||||
NS_ConvertUTF16toUTF8(aName).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -684,7 +748,10 @@ void CustomElementRegistry::Define(
|
||||
* throw a "NotSupportedError" DOMException and abort these steps.
|
||||
*/
|
||||
if (mCustomDefinitions.GetWeak(nameAtom)) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
nsPrintfCString("'%s' has already been defined as a custom element",
|
||||
NS_ConvertUTF16toUTF8(aName).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -697,7 +764,12 @@ void CustomElementRegistry::Define(
|
||||
if (ptr) {
|
||||
MOZ_ASSERT(mCustomDefinitions.GetWeak(ptr->value()),
|
||||
"Definition must be found in mCustomDefinitions");
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
nsAutoCString name;
|
||||
ptr->value()->ToUTF8String(name);
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
nsPrintfCString("'%s' and '%s' have the same constructor",
|
||||
NS_ConvertUTF16toUTF8(aName).get(), name.get()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -729,7 +801,10 @@ void CustomElementRegistry::Define(
|
||||
if (aOptions.mExtends.WasPassed()) {
|
||||
RefPtr<nsAtom> extendsAtom(NS_Atomize(aOptions.mExtends.Value()));
|
||||
if (nsContentUtils::IsCustomElementName(extendsAtom, kNameSpaceID_XHTML)) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
nsPrintfCString("'%s' cannot extend a custom element",
|
||||
NS_ConvertUTF16toUTF8(aName).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -758,12 +833,17 @@ void CustomElementRegistry::Define(
|
||||
* set, then throw a "NotSupportedError" DOMException and abort these steps.
|
||||
*/
|
||||
if (mIsCustomDefinitionRunning) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
"Cannot define a custom element while defining another custom element");
|
||||
return;
|
||||
}
|
||||
|
||||
auto callbacksHolder = MakeUnique<LifecycleCallbacks>();
|
||||
nsTArray<RefPtr<nsAtom>> observedAttributes;
|
||||
AutoTArray<RefPtr<nsAtom>, 2> disabledFeatures;
|
||||
bool disableInternals = false;
|
||||
bool disableShadow = false;
|
||||
{ // Set mIsCustomDefinitionRunning.
|
||||
/**
|
||||
* 9. Set this CustomElementRegistry's element definition is running flag.
|
||||
@ -771,7 +851,7 @@ void CustomElementRegistry::Define(
|
||||
AutoSetRunningFlag as(this);
|
||||
|
||||
/**
|
||||
* 10.1. Let prototype be Get(constructor, "prototype"). Rethrow any
|
||||
* 14.1. Let prototype be Get(constructor, "prototype"). Rethrow any
|
||||
* exceptions.
|
||||
*/
|
||||
// The .prototype on the constructor passed could be an "expando" of a
|
||||
@ -784,7 +864,7 @@ void CustomElementRegistry::Define(
|
||||
}
|
||||
|
||||
/**
|
||||
* 10.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()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_OBJECT>(
|
||||
@ -793,12 +873,12 @@ void CustomElementRegistry::Define(
|
||||
}
|
||||
|
||||
/**
|
||||
* 10.3. Let lifecycleCallbacks be a map with the four keys
|
||||
* 14.3. Let lifecycleCallbacks be a map with the four keys
|
||||
* "connectedCallback", "disconnectedCallback", "adoptedCallback", and
|
||||
* "attributeChangedCallback", each of which belongs to an entry whose
|
||||
* value is null. The 'getCustomInterface' callback is also included
|
||||
* for chrome usage.
|
||||
* 10.4. For each of the four keys callbackName in lifecycleCallbacks:
|
||||
* 14.4. For each of the four keys callbackName in lifecycleCallbacks:
|
||||
* 1. Let callbackValue be Get(prototype, callbackName). Rethrow any
|
||||
* exceptions.
|
||||
* 2. If callbackValue is not undefined, then set the value of the
|
||||
@ -812,8 +892,7 @@ void CustomElementRegistry::Define(
|
||||
}
|
||||
|
||||
/**
|
||||
* 10.5. Let observedAttributes be an empty sequence<DOMString>.
|
||||
* 10.6. If the value of the entry in lifecycleCallbacks with key
|
||||
* 14.5. If the value of the entry in lifecycleCallbacks with key
|
||||
* "attributeChangedCallback" is not null, then:
|
||||
* 1. Let observedAttributesIterable be Get(constructor,
|
||||
* "observedAttributes"). Rethrow any exceptions.
|
||||
@ -823,63 +902,41 @@ void CustomElementRegistry::Define(
|
||||
* any exceptions from the conversion.
|
||||
*/
|
||||
if (callbacksHolder->mAttributeChangedCallback.WasPassed()) {
|
||||
JS::Rooted<JS::Value> observedAttributesIterable(aCx);
|
||||
|
||||
if (!JS_GetProperty(aCx, constructor, "observedAttributes",
|
||||
&observedAttributesIterable)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!observedAttributesIterable.isUndefined()) {
|
||||
if (!observedAttributesIterable.isObject()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
|
||||
NS_LITERAL_STRING("observedAttributes"));
|
||||
return;
|
||||
}
|
||||
|
||||
JS::ForOfIterator iter(aCx);
|
||||
if (!iter.init(observedAttributesIterable,
|
||||
JS::ForOfIterator::AllowNonIterable)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iter.valueIsIterable()) {
|
||||
aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
|
||||
NS_LITERAL_STRING("observedAttributes"));
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> attribute(aCx);
|
||||
while (true) {
|
||||
bool done;
|
||||
if (!iter.next(&attribute, &done)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsAutoString attrStr;
|
||||
if (!ConvertJSValueToString(aCx, attribute, eStringify, eStringify,
|
||||
attrStr)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!observedAttributes.AppendElement(NS_Atomize(attrStr))) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
if (!JSObjectToAtomArray(aCx, constructor, u"observedAttributes",
|
||||
observedAttributes, aRv)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 14.6. Let disabledFeatures be an empty sequence<DOMString>.
|
||||
* 14.7. Let disabledFeaturesIterable be Get(constructor,
|
||||
* "disabledFeatures"). Rethrow any exceptions.
|
||||
* 14.8. If disabledFeaturesIterable is not undefined, then set
|
||||
* disabledFeatures to the result of converting
|
||||
* disabledFeaturesIterable to a sequence<DOMString>.
|
||||
* Rethrow any exceptions from the conversion.
|
||||
*/
|
||||
if (StaticPrefs::dom_webcomponents_elementInternals_enabled()) {
|
||||
if (!JSObjectToAtomArray(aCx, constructor, u"disabledFeatures",
|
||||
disabledFeatures, aRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 14.9. Set disableInternals to true if disabledFeaturesSequence contains
|
||||
// "internals".
|
||||
disableInternals = disabledFeatures.Contains(
|
||||
static_cast<nsStaticAtom*>(nsGkAtoms::internals));
|
||||
|
||||
// 14.10. Set disableShadow to true if disabledFeaturesSequence contains
|
||||
// "shadow".
|
||||
disableShadow = disabledFeatures.Contains(
|
||||
static_cast<nsStaticAtom*>(nsGkAtoms::shadow));
|
||||
}
|
||||
} // Unset mIsCustomDefinitionRunning
|
||||
|
||||
/**
|
||||
* 11. Let definition be a new custom element definition with name name,
|
||||
* 15. Let definition be a new custom element definition with name name,
|
||||
* local name localName, constructor constructor, prototype prototype,
|
||||
* observed attributes observedAttributes, and lifecycle callbacks
|
||||
* lifecycleCallbacks.
|
||||
@ -888,7 +945,7 @@ void CustomElementRegistry::Define(
|
||||
RefPtr<nsAtom> localNameAtom(NS_Atomize(localName));
|
||||
|
||||
/**
|
||||
* 12. Add definition to this CustomElementRegistry.
|
||||
* 16. Add definition to this CustomElementRegistry.
|
||||
*/
|
||||
if (!mConstructors.put(constructorUnwrapped, nameAtom)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
@ -897,7 +954,8 @@ void CustomElementRegistry::Define(
|
||||
|
||||
RefPtr<CustomElementDefinition> definition = new CustomElementDefinition(
|
||||
nameAtom, localNameAtom, nameSpaceID, &aFunctionConstructor,
|
||||
std::move(observedAttributes), std::move(callbacksHolder));
|
||||
std::move(observedAttributes), std::move(callbacksHolder),
|
||||
disableInternals, disableShadow);
|
||||
|
||||
CustomElementDefinition* def = definition.get();
|
||||
mCustomDefinitions.Put(nameAtom, definition.forget());
|
||||
@ -906,12 +964,12 @@ void CustomElementRegistry::Define(
|
||||
"Number of entries should be the same");
|
||||
|
||||
/**
|
||||
* 13. 14. 15. Upgrade candidates
|
||||
* 17. 18. 19. Upgrade candidates
|
||||
*/
|
||||
UpgradeCandidates(nameAtom, def, aRv);
|
||||
|
||||
/**
|
||||
* 16. If this CustomElementRegistry's when-defined promise map contains an
|
||||
* 20. If this CustomElementRegistry's when-defined promise map contains an
|
||||
* entry with key name:
|
||||
* 1. Let promise be the value of that entry.
|
||||
* 2. Resolve promise with undefined.
|
||||
@ -1034,8 +1092,19 @@ already_AddRefed<Promise> CustomElementRegistry::WhenDefined(
|
||||
namespace {
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
static void DoUpgrade(Element* aElement, CustomElementConstructor* aConstructor,
|
||||
static void DoUpgrade(Element* aElement, CustomElementDefinition* aDefinition,
|
||||
CustomElementConstructor* aConstructor,
|
||||
ErrorResult& aRv) {
|
||||
if (aDefinition->mDisableShadow && aElement->GetShadowRoot()) {
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
nsPrintfCString(
|
||||
"Custom element upgrade to '%s' is disabled due to shadow root "
|
||||
"already exists",
|
||||
NS_ConvertUTF16toUTF8(aDefinition->mType->GetUTF16String()).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> constructResult(RootingCx());
|
||||
// Rethrow the exception since it might actually throw the exception from the
|
||||
// upgrade steps back out to the caller of document.createElement.
|
||||
@ -1106,7 +1175,8 @@ void CustomElementRegistry::Upgrade(Element* aElement,
|
||||
AutoConstructionStackEntry acs(aDefinition->mConstructionStack, aElement);
|
||||
|
||||
// Step 6 and step 7.
|
||||
DoUpgrade(aElement, MOZ_KnownLive(aDefinition->mConstructor), aRv);
|
||||
DoUpgrade(aElement, aDefinition, MOZ_KnownLive(aDefinition->mConstructor),
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
data->mState = CustomElementData::State::eFailed;
|
||||
// Empty element's custom element reaction queue.
|
||||
@ -1387,13 +1457,16 @@ CustomElementDefinition::CustomElementDefinition(
|
||||
nsAtom* aType, nsAtom* aLocalName, int32_t aNamespaceID,
|
||||
CustomElementConstructor* aConstructor,
|
||||
nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
|
||||
UniquePtr<LifecycleCallbacks>&& aCallbacks)
|
||||
UniquePtr<LifecycleCallbacks>&& aCallbacks, bool aDisableInternals,
|
||||
bool aDisableShadow)
|
||||
: mType(aType),
|
||||
mLocalName(aLocalName),
|
||||
mNamespaceID(aNamespaceID),
|
||||
mConstructor(aConstructor),
|
||||
mObservedAttributes(std::move(aObservedAttributes)),
|
||||
mCallbacks(std::move(aCallbacks)) {}
|
||||
mCallbacks(std::move(aCallbacks)),
|
||||
mDisableInternals(aDisableInternals),
|
||||
mDisableShadow(aDisableShadow) {}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -103,6 +103,8 @@ struct CustomElementData {
|
||||
void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
|
||||
CustomElementDefinition* GetCustomElementDefinition();
|
||||
nsAtom* GetCustomElementType() const { return mType; }
|
||||
void AttachedInternals();
|
||||
bool HasAttachedInternals() const { return mIsAttachedInternals; }
|
||||
|
||||
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
|
||||
void Unlink();
|
||||
@ -121,6 +123,7 @@ struct CustomElementData {
|
||||
// this would be x-button.
|
||||
RefPtr<nsAtom> mType;
|
||||
RefPtr<CustomElementDefinition> mCustomElementDefinition;
|
||||
bool mIsAttachedInternals = false;
|
||||
};
|
||||
|
||||
#define ALREADY_CONSTRUCTED_MARKER nullptr
|
||||
@ -135,7 +138,8 @@ struct CustomElementDefinition {
|
||||
int32_t aNamespaceID,
|
||||
CustomElementConstructor* aConstructor,
|
||||
nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
|
||||
UniquePtr<LifecycleCallbacks>&& aCallbacks);
|
||||
UniquePtr<LifecycleCallbacks>&& aCallbacks,
|
||||
bool aDisableInternals, bool aDisableShadow);
|
||||
|
||||
// The type (name) for this custom element, for <button is="x-foo"> or <x-foo>
|
||||
// this would be x-foo.
|
||||
@ -156,6 +160,12 @@ struct CustomElementDefinition {
|
||||
// The lifecycle callbacks to call for this custom element.
|
||||
UniquePtr<LifecycleCallbacks> mCallbacks;
|
||||
|
||||
// Determine whether to allow to attachInternals() for this custom element.
|
||||
bool mDisableInternals = false;
|
||||
|
||||
// Determine whether to allow to attachShadow() for this custom element.
|
||||
bool mDisableShadow = false;
|
||||
|
||||
// A construction stack. Use nullptr to represent an "already constructed
|
||||
// marker".
|
||||
nsTArray<RefPtr<Element>> mConstructionStack;
|
||||
@ -466,6 +476,10 @@ class CustomElementRegistry final : public nsISupports, public nsWrapperCache {
|
||||
private:
|
||||
~CustomElementRegistry();
|
||||
|
||||
bool JSObjectToAtomArray(JSContext* aCx, JS::Handle<JSObject*> aConstructor,
|
||||
const char16_t* aName,
|
||||
nsTArray<RefPtr<nsAtom>>& aArray, ErrorResult& aRv);
|
||||
|
||||
static UniquePtr<CustomElementCallback> CreateCustomElementCallback(
|
||||
Document::ElementCallbackType aType, Element* aCustomElement,
|
||||
LifecycleCallbackArgs* aArgs,
|
||||
|
@ -1,71 +0,0 @@
|
||||
/* 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 "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/DOMErrorBinding.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/UseCounter.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMError, mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMError)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMError)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMError)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(DOMError)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
DOMError::DOMError(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
|
||||
|
||||
DOMError::DOMError(nsPIDOMWindowInner* aWindow, nsresult aValue)
|
||||
: mWindow(aWindow) {
|
||||
nsCString name, message;
|
||||
NS_GetNameAndMessageForDOMNSResult(aValue, name, message);
|
||||
|
||||
CopyUTF8toUTF16(name, mName);
|
||||
CopyUTF8toUTF16(message, mMessage);
|
||||
}
|
||||
|
||||
DOMError::DOMError(nsPIDOMWindowInner* aWindow, const nsAString& aName)
|
||||
: mWindow(aWindow), mName(aName) {}
|
||||
|
||||
DOMError::DOMError(nsPIDOMWindowInner* aWindow, const nsAString& aName,
|
||||
const nsAString& aMessage)
|
||||
: mWindow(aWindow), mName(aName), mMessage(aMessage) {}
|
||||
|
||||
DOMError::~DOMError() {}
|
||||
|
||||
JSObject* DOMError::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return DOMError_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<DOMError> DOMError::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aName,
|
||||
const nsAString& aMessage,
|
||||
ErrorResult& aRv) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
if (window) {
|
||||
nsCOMPtr<Document> doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
doc->SetDocumentAndPageUseCounter(eUseCounter_custom_DOMErrorConstructor);
|
||||
}
|
||||
}
|
||||
|
||||
// Window is null for chrome code.
|
||||
|
||||
RefPtr<DOMError> ret = new DOMError(window, aName, aMessage);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,76 +0,0 @@
|
||||
/* 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_domerror_h__
|
||||
#define mozilla_dom_domerror_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#define DOMERROR_IID \
|
||||
{ \
|
||||
0x220cb63f, 0xa37d, 0x4ba4, { \
|
||||
0x8e, 0x31, 0xfc, 0xde, 0xec, 0x48, 0xe1, 0x66 \
|
||||
} \
|
||||
}
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
class DOMError : public nsISupports, public nsWrapperCache {
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsString mName;
|
||||
nsString mMessage;
|
||||
|
||||
protected:
|
||||
virtual ~DOMError();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMError)
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(DOMERROR_IID)
|
||||
|
||||
// aWindow can be null if this DOMError is not associated with a particular
|
||||
// window.
|
||||
|
||||
explicit DOMError(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
DOMError(nsPIDOMWindowInner* aWindow, nsresult aValue);
|
||||
|
||||
DOMError(nsPIDOMWindowInner* aWindow, const nsAString& aName);
|
||||
|
||||
DOMError(nsPIDOMWindowInner* aWindow, const nsAString& aName,
|
||||
const nsAString& aMessage);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<DOMError> Constructor(const GlobalObject& global,
|
||||
const nsAString& name,
|
||||
const nsAString& message,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetName(nsString& aRetval) const { aRetval = mName; }
|
||||
|
||||
void GetMessage(nsString& aRetval) const { aRetval = mMessage; }
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(DOMError, DOMERROR_IID)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_domerror_h__
|
@ -356,7 +356,7 @@ void DOMException::GetName(nsString& retval) { CopyUTF8toUTF16(mName, retval); }
|
||||
|
||||
already_AddRefed<DOMException> DOMException::Constructor(
|
||||
GlobalObject& /* unused */, const nsAString& aMessage,
|
||||
const Optional<nsAString>& aName, ErrorResult& aError) {
|
||||
const Optional<nsAString>& aName) {
|
||||
nsresult exceptionResult = NS_OK;
|
||||
uint16_t exceptionCode = 0;
|
||||
nsCString name(NS_LITERAL_CSTRING("Error"));
|
||||
|
@ -145,7 +145,7 @@ class DOMException : public Exception {
|
||||
|
||||
static already_AddRefed<DOMException> Constructor(
|
||||
GlobalObject& /* unused */, const nsAString& aMessage,
|
||||
const Optional<nsAString>& aName, ErrorResult& aError);
|
||||
const Optional<nsAString>& aName);
|
||||
|
||||
uint16_t Code() const { return mCode; }
|
||||
|
||||
|
@ -59,16 +59,14 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
|
||||
const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb, mozilla::ErrorResult& aRv) {
|
||||
const GlobalObject& aGlobal, dom::IntersectionCallback& aCb,
|
||||
ErrorResult& aRv) {
|
||||
return Constructor(aGlobal, aCb, IntersectionObserverInit(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
|
||||
const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb,
|
||||
const mozilla::dom::IntersectionObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv) {
|
||||
const GlobalObject& aGlobal, dom::IntersectionCallback& aCb,
|
||||
const IntersectionObserverInit& aOptions, ErrorResult& aRv) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
@ -81,15 +79,12 @@ already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
|
||||
observer->mRoot = aOptions.mRoot;
|
||||
|
||||
if (!observer->SetRootMargin(aOptions.mRootMargin)) {
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_SYNTAX_ERR,
|
||||
NS_LITERAL_CSTRING(
|
||||
"rootMargin must be specified in pixels or percent."));
|
||||
aRv.ThrowSyntaxError("rootMargin must be specified in pixels or percent.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mThreshold.IsDoubleSequence()) {
|
||||
const mozilla::dom::Sequence<double>& thresholds =
|
||||
const Sequence<double>& thresholds =
|
||||
aOptions.mThreshold.GetAsDoubleSequence();
|
||||
observer->mThresholds.SetCapacity(thresholds.Length());
|
||||
for (const auto& thresh : thresholds) {
|
||||
@ -116,7 +111,7 @@ bool DOMIntersectionObserver::SetRootMargin(const nsAString& aString) {
|
||||
return Servo_IntersectionObserverRootMargin_Parse(&aString, &mRootMargin);
|
||||
}
|
||||
|
||||
void DOMIntersectionObserver::GetRootMargin(mozilla::dom::DOMString& aRetVal) {
|
||||
void DOMIntersectionObserver::GetRootMargin(DOMString& aRetVal) {
|
||||
nsString& retVal = aRetVal;
|
||||
Servo_IntersectionObserverRootMargin_ToString(&mRootMargin, &retVal);
|
||||
}
|
||||
@ -451,8 +446,7 @@ void DOMIntersectionObserver::Notify() {
|
||||
if (!mQueuedEntries.Length()) {
|
||||
return;
|
||||
}
|
||||
mozilla::dom::Sequence<mozilla::OwningNonNull<DOMIntersectionObserverEntry>>
|
||||
entries;
|
||||
Sequence<OwningNonNull<DOMIntersectionObserverEntry>> entries;
|
||||
if (entries.SetCapacity(mQueuedEntries.Length(), mozilla::fallible)) {
|
||||
for (size_t i = 0; i < mQueuedEntries.Length(); ++i) {
|
||||
RefPtr<DOMIntersectionObserverEntry> next = mQueuedEntries[i];
|
||||
|
@ -7,12 +7,9 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/IntersectionObserverBinding.h"
|
||||
#include "nsStyleCoord.h"
|
||||
#include "mozilla/ServoStyleConsts.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
using mozilla::dom::DOMRect;
|
||||
using mozilla::dom::Element;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -42,13 +39,12 @@ class DOMIntersectionObserverEntry final : public nsISupports,
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override {
|
||||
return mozilla::dom::IntersectionObserverEntry_Binding::Wrap(aCx, this,
|
||||
aGivenProto);
|
||||
return IntersectionObserverEntry_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp Time() { return mTime; }
|
||||
DOMHighResTimeStamp Time() const { return mTime; }
|
||||
|
||||
DOMRect* GetRootBounds() { return mRootBounds; }
|
||||
|
||||
@ -56,9 +52,9 @@ class DOMIntersectionObserverEntry final : public nsISupports,
|
||||
|
||||
DOMRect* IntersectionRect() { return mIntersectionRect; }
|
||||
|
||||
bool IsIntersecting() { return mIsIntersecting; }
|
||||
bool IsIntersecting() const { return mIsIntersecting; }
|
||||
|
||||
double IntersectionRatio() { return mIntersectionRatio; }
|
||||
double IntersectionRatio() const { return mIntersectionRatio; }
|
||||
|
||||
Element* Target() { return mTarget; }
|
||||
|
||||
@ -86,7 +82,7 @@ class DOMIntersectionObserver final : public nsISupports,
|
||||
|
||||
public:
|
||||
DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
|
||||
mozilla::dom::IntersectionCallback& aCb)
|
||||
dom::IntersectionCallback& aCb)
|
||||
: mOwner(aOwner),
|
||||
mDocument(mOwner->GetExtantDoc()),
|
||||
mCallback(&aCb),
|
||||
@ -96,25 +92,21 @@ class DOMIntersectionObserver final : public nsISupports,
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_INTERSECTION_OBSERVER_IID)
|
||||
|
||||
static already_AddRefed<DOMIntersectionObserver> Constructor(
|
||||
const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb, mozilla::ErrorResult& aRv);
|
||||
const GlobalObject&, dom::IntersectionCallback&, ErrorResult&);
|
||||
static already_AddRefed<DOMIntersectionObserver> Constructor(
|
||||
const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb,
|
||||
const mozilla::dom::IntersectionObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv);
|
||||
const GlobalObject&, dom::IntersectionCallback&,
|
||||
const IntersectionObserverInit&, ErrorResult&);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override {
|
||||
return mozilla::dom::IntersectionObserver_Binding::Wrap(aCx, this,
|
||||
aGivenProto);
|
||||
return IntersectionObserver_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
Element* GetRoot() const { return mRoot; }
|
||||
|
||||
void GetRootMargin(mozilla::dom::DOMString& aRetVal);
|
||||
void GetRootMargin(DOMString& aRetVal);
|
||||
void GetThresholds(nsTArray<double>& aRetVal);
|
||||
void Observe(Element& aTarget);
|
||||
void Unobserve(Element& aTarget);
|
||||
@ -124,9 +116,7 @@ class DOMIntersectionObserver final : public nsISupports,
|
||||
|
||||
void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
|
||||
|
||||
mozilla::dom::IntersectionCallback* IntersectionCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
dom::IntersectionCallback* IntersectionCallback() { return mCallback; }
|
||||
|
||||
bool SetRootMargin(const nsAString& aString);
|
||||
|
||||
@ -144,7 +134,7 @@ class DOMIntersectionObserver final : public nsISupports,
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mOwner;
|
||||
RefPtr<Document> mDocument;
|
||||
RefPtr<mozilla::dom::IntersectionCallback> mCallback;
|
||||
RefPtr<dom::IntersectionCallback> mCallback;
|
||||
RefPtr<Element> mRoot;
|
||||
StyleRect<LengthPercentage> mRootMargin;
|
||||
nsTArray<double> mThresholds;
|
||||
|
@ -17,6 +17,9 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "js/Conversions.h" // JS::NumberToString
|
||||
#include "js/Equality.h" // JS::SameValueZero
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -36,29 +39,235 @@ JSObject* DOMMatrixReadOnly::WrapObject(JSContext* aCx,
|
||||
return DOMMatrixReadOnly_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup-2d
|
||||
static bool ValidateAndFixupMatrix2DInit(DOMMatrix2DInit& aMatrixInit,
|
||||
ErrorResult& aRv) {
|
||||
#define ValidateAliases(field, alias, fieldName, aliasName) \
|
||||
if ((field).WasPassed() && (alias).WasPassed() && \
|
||||
!JS::SameValueZero((field).Value(), (alias).Value())) { \
|
||||
aRv.ThrowTypeError<MSG_MATRIX_INIT_CONFLICTING_VALUE>((fieldName), \
|
||||
(aliasName)); \
|
||||
return false; \
|
||||
}
|
||||
#define SetFromAliasOrDefault(field, alias, defaultValue) \
|
||||
if (!(field).WasPassed()) { \
|
||||
if ((alias).WasPassed()) { \
|
||||
(field).Construct((alias).Value()); \
|
||||
} else { \
|
||||
(field).Construct(defaultValue); \
|
||||
} \
|
||||
}
|
||||
#define ValidateAndSet(field, alias, fieldName, aliasName, defaultValue) \
|
||||
ValidateAliases((field), (alias), NS_LITERAL_STRING(fieldName), \
|
||||
NS_LITERAL_STRING(aliasName)); \
|
||||
SetFromAliasOrDefault((field), (alias), (defaultValue));
|
||||
|
||||
ValidateAndSet(aMatrixInit.mM11, aMatrixInit.mA, "m11", "a", 1);
|
||||
ValidateAndSet(aMatrixInit.mM12, aMatrixInit.mB, "m12", "b", 0);
|
||||
ValidateAndSet(aMatrixInit.mM21, aMatrixInit.mC, "m21", "c", 0);
|
||||
ValidateAndSet(aMatrixInit.mM22, aMatrixInit.mD, "m22", "d", 1);
|
||||
ValidateAndSet(aMatrixInit.mM41, aMatrixInit.mE, "m41", "e", 0);
|
||||
ValidateAndSet(aMatrixInit.mM42, aMatrixInit.mF, "m42", "f", 0);
|
||||
|
||||
return true;
|
||||
|
||||
#undef ValidateAliases
|
||||
#undef SetFromAliasOrDefault
|
||||
#undef ValidateAndSet
|
||||
}
|
||||
|
||||
// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
|
||||
static bool ValidateAndFixupMatrixInit(DOMMatrixInit& aMatrixInit,
|
||||
ErrorResult& aRv) {
|
||||
#define Check3DField(field, fieldName, defaultValue) \
|
||||
if ((field) != (defaultValue)) { \
|
||||
if (!aMatrixInit.mIs2D.WasPassed()) { \
|
||||
aMatrixInit.mIs2D.Construct(false); \
|
||||
return true; \
|
||||
} \
|
||||
if (aMatrixInit.mIs2D.Value()) { \
|
||||
aRv.ThrowTypeError<MSG_MATRIX_INIT_EXCEEDS_2D>( \
|
||||
NS_LITERAL_STRING(fieldName)); \
|
||||
return false; \
|
||||
} \
|
||||
}
|
||||
|
||||
if (!ValidateAndFixupMatrix2DInit(aMatrixInit, aRv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Check3DField(aMatrixInit.mM13, "m13", 0);
|
||||
Check3DField(aMatrixInit.mM14, "m14", 0);
|
||||
Check3DField(aMatrixInit.mM23, "m23", 0);
|
||||
Check3DField(aMatrixInit.mM24, "m24", 0);
|
||||
Check3DField(aMatrixInit.mM31, "m31", 0);
|
||||
Check3DField(aMatrixInit.mM32, "m32", 0);
|
||||
Check3DField(aMatrixInit.mM34, "m34", 0);
|
||||
Check3DField(aMatrixInit.mM43, "m43", 0);
|
||||
Check3DField(aMatrixInit.mM33, "m33", 1);
|
||||
Check3DField(aMatrixInit.mM44, "m44", 1);
|
||||
|
||||
if (!aMatrixInit.mIs2D.WasPassed()) {
|
||||
aMatrixInit.mIs2D.Construct(true);
|
||||
}
|
||||
return true;
|
||||
|
||||
#undef Check3DField
|
||||
}
|
||||
|
||||
void DOMMatrixReadOnly::SetDataFromMatrix2DInit(
|
||||
const DOMMatrix2DInit& aMatrixInit) {
|
||||
MOZ_ASSERT(Is2D());
|
||||
mMatrix2D->_11 = aMatrixInit.mM11.Value();
|
||||
mMatrix2D->_12 = aMatrixInit.mM12.Value();
|
||||
mMatrix2D->_21 = aMatrixInit.mM21.Value();
|
||||
mMatrix2D->_22 = aMatrixInit.mM22.Value();
|
||||
mMatrix2D->_31 = aMatrixInit.mM41.Value();
|
||||
mMatrix2D->_32 = aMatrixInit.mM42.Value();
|
||||
}
|
||||
|
||||
void DOMMatrixReadOnly::SetDataFromMatrixInit(
|
||||
const DOMMatrixInit& aMatrixInit) {
|
||||
const bool is2D = aMatrixInit.mIs2D.Value();
|
||||
MOZ_ASSERT(is2D == Is2D());
|
||||
if (is2D) {
|
||||
SetDataFromMatrix2DInit(aMatrixInit);
|
||||
} else {
|
||||
mMatrix3D->_11 = aMatrixInit.mM11.Value();
|
||||
mMatrix3D->_12 = aMatrixInit.mM12.Value();
|
||||
mMatrix3D->_13 = aMatrixInit.mM13;
|
||||
mMatrix3D->_14 = aMatrixInit.mM14;
|
||||
mMatrix3D->_21 = aMatrixInit.mM21.Value();
|
||||
mMatrix3D->_22 = aMatrixInit.mM22.Value();
|
||||
mMatrix3D->_23 = aMatrixInit.mM23;
|
||||
mMatrix3D->_24 = aMatrixInit.mM24;
|
||||
mMatrix3D->_31 = aMatrixInit.mM31;
|
||||
mMatrix3D->_32 = aMatrixInit.mM32;
|
||||
mMatrix3D->_33 = aMatrixInit.mM33;
|
||||
mMatrix3D->_34 = aMatrixInit.mM34;
|
||||
mMatrix3D->_41 = aMatrixInit.mM41.Value();
|
||||
mMatrix3D->_42 = aMatrixInit.mM42.Value();
|
||||
mMatrix3D->_43 = aMatrixInit.mM43;
|
||||
mMatrix3D->_44 = aMatrixInit.mM44;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromMatrix(
|
||||
nsISupports* aParent, const DOMMatrix2DInit& aMatrixInit,
|
||||
ErrorResult& aRv) {
|
||||
DOMMatrix2DInit matrixInit(aMatrixInit);
|
||||
if (!ValidateAndFixupMatrix2DInit(matrixInit, aRv)) {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
RefPtr<DOMMatrixReadOnly> matrix =
|
||||
new DOMMatrixReadOnly(aParent, /* is2D */ true);
|
||||
matrix->SetDataFromMatrix2DInit(matrixInit);
|
||||
return matrix.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromMatrix(
|
||||
nsISupports* aParent, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv) {
|
||||
DOMMatrixInit matrixInit(aMatrixInit);
|
||||
if (!ValidateAndFixupMatrixInit(matrixInit, aRv)) {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
RefPtr<DOMMatrixReadOnly> rval =
|
||||
new DOMMatrixReadOnly(aParent, matrixInit.mIs2D.Value());
|
||||
rval->SetDataFromMatrixInit(matrixInit);
|
||||
return rval.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromMatrix(
|
||||
const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrixReadOnly> matrix =
|
||||
FromMatrix(aGlobal.GetAsSupports(), aMatrixInit, aRv);
|
||||
return matrix.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv) {
|
||||
aArray32.ComputeLengthAndData();
|
||||
|
||||
const int length = aArray32.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrixReadOnly> obj =
|
||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray32.Data(), length, aRv);
|
||||
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv) {
|
||||
aArray64.ComputeLengthAndData();
|
||||
|
||||
const int length = aArray64.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrixReadOnly> obj =
|
||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray64.Data(), length, aRv);
|
||||
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
const Optional<StringOrUnrestrictedDoubleSequence>& aArg,
|
||||
const Optional<UTF8StringOrUnrestrictedDoubleSequenceOrDOMMatrixReadOnly>&
|
||||
aArg,
|
||||
ErrorResult& aRv) {
|
||||
if (!aArg.WasPassed()) {
|
||||
RefPtr<DOMMatrixReadOnly> rval =
|
||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports());
|
||||
if (!aArg.WasPassed()) {
|
||||
return rval.forget();
|
||||
}
|
||||
|
||||
const auto& arg = aArg.Value();
|
||||
if (arg.IsString()) {
|
||||
if (arg.IsUTF8String()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> win =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!win) {
|
||||
aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
|
||||
return nullptr;
|
||||
}
|
||||
rval->SetMatrixValue(arg.GetAsString(), aRv);
|
||||
} else {
|
||||
const auto& sequence = arg.GetAsUnrestrictedDoubleSequence();
|
||||
SetDataInMatrix(rval, sequence.Elements(), sequence.Length(), aRv);
|
||||
RefPtr<DOMMatrixReadOnly> rval =
|
||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports());
|
||||
rval->SetMatrixValue(arg.GetAsUTF8String(), aRv);
|
||||
return rval.forget();
|
||||
}
|
||||
if (arg.IsDOMMatrixReadOnly()) {
|
||||
RefPtr<DOMMatrixReadOnly> obj = new DOMMatrixReadOnly(
|
||||
aGlobal.GetAsSupports(), arg.GetAsDOMMatrixReadOnly());
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
const auto& sequence = arg.GetAsUnrestrictedDoubleSequence();
|
||||
const int length = sequence.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrixReadOnly> rval =
|
||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(rval, sequence.Elements(), length, aRv);
|
||||
return rval.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader) {
|
||||
uint8_t is2D;
|
||||
|
||||
if (!JS_ReadBytes(aReader, &is2D, 1)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DOMMatrixReadOnly> rval = new DOMMatrixReadOnly(aGlobal, is2D);
|
||||
|
||||
if (!ReadStructuredCloneElements(aReader, rval)) {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
return rval.forget();
|
||||
}
|
||||
@ -71,11 +280,11 @@ already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Translate(double aTx, double aTy,
|
||||
return retval.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Scale(double aScale,
|
||||
double aOriginX,
|
||||
double aOriginY) const {
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Scale(
|
||||
double aScaleX, const Optional<double>& aScaleY, double aScaleZ,
|
||||
double aOriginX, double aOriginY, double aOriginZ) const {
|
||||
RefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
|
||||
retval->ScaleSelf(aScale, aOriginX, aOriginY);
|
||||
retval->ScaleSelf(aScaleX, aScaleY, aScaleZ, aOriginX, aOriginY, aOriginZ);
|
||||
|
||||
return retval.forget();
|
||||
}
|
||||
@ -91,20 +300,18 @@ already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Scale3d(double aScale,
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::ScaleNonUniform(
|
||||
double aScaleX, double aScaleY, double aScaleZ, double aOriginX,
|
||||
double aOriginY, double aOriginZ) const {
|
||||
double aScaleX, double aScaleY) const {
|
||||
RefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
|
||||
retval->ScaleNonUniformSelf(aScaleX, aScaleY, aScaleZ, aOriginX, aOriginY,
|
||||
aOriginZ);
|
||||
retval->ScaleSelf(aScaleX, Optional<double>(aScaleY), 1, 0, 0, 0);
|
||||
|
||||
return retval.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Rotate(double aAngle,
|
||||
double aOriginX,
|
||||
double aOriginY) const {
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Rotate(
|
||||
double aRotX, const Optional<double>& aRotY,
|
||||
const Optional<double>& aRotZ) const {
|
||||
RefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
|
||||
retval->RotateSelf(aAngle, aOriginX, aOriginY);
|
||||
retval->RotateSelf(aRotX, aRotY, aRotZ);
|
||||
|
||||
return retval.forget();
|
||||
}
|
||||
@ -140,9 +347,9 @@ already_AddRefed<DOMMatrix> DOMMatrixReadOnly::SkewY(double aSy) const {
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Multiply(
|
||||
const DOMMatrix& other) const {
|
||||
const DOMMatrixInit& other, ErrorResult& aRv) const {
|
||||
RefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
|
||||
retval->MultiplySelf(other);
|
||||
retval->MultiplySelf(other, aRv);
|
||||
|
||||
return retval.forget();
|
||||
}
|
||||
@ -150,13 +357,13 @@ already_AddRefed<DOMMatrix> DOMMatrixReadOnly::Multiply(
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::FlipX() const {
|
||||
RefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
|
||||
if (mMatrix3D) {
|
||||
gfx::Matrix4x4 m;
|
||||
gfx::Matrix4x4Double m;
|
||||
m._11 = -1;
|
||||
retval->mMatrix3D = new gfx::Matrix4x4(m * *mMatrix3D);
|
||||
retval->mMatrix3D = new gfx::Matrix4x4Double(m * *mMatrix3D);
|
||||
} else {
|
||||
gfx::Matrix m;
|
||||
gfx::MatrixDouble m;
|
||||
m._11 = -1;
|
||||
retval->mMatrix2D = new gfx::Matrix(mMatrix2D ? m * *mMatrix2D : m);
|
||||
retval->mMatrix2D = new gfx::MatrixDouble(mMatrix2D ? m * *mMatrix2D : m);
|
||||
}
|
||||
|
||||
return retval.forget();
|
||||
@ -165,13 +372,13 @@ already_AddRefed<DOMMatrix> DOMMatrixReadOnly::FlipX() const {
|
||||
already_AddRefed<DOMMatrix> DOMMatrixReadOnly::FlipY() const {
|
||||
RefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
|
||||
if (mMatrix3D) {
|
||||
gfx::Matrix4x4 m;
|
||||
gfx::Matrix4x4Double m;
|
||||
m._22 = -1;
|
||||
retval->mMatrix3D = new gfx::Matrix4x4(m * *mMatrix3D);
|
||||
retval->mMatrix3D = new gfx::Matrix4x4Double(m * *mMatrix3D);
|
||||
} else {
|
||||
gfx::Matrix m;
|
||||
gfx::MatrixDouble m;
|
||||
m._22 = -1;
|
||||
retval->mMatrix2D = new gfx::Matrix(mMatrix2D ? m * *mMatrix2D : m);
|
||||
retval->mMatrix2D = new gfx::MatrixDouble(mMatrix2D ? m * *mMatrix2D : m);
|
||||
}
|
||||
|
||||
return retval.forget();
|
||||
@ -212,9 +419,9 @@ already_AddRefed<DOMPoint> DOMMatrixReadOnly::TransformPoint(
|
||||
retval->SetZ(transformedPoint.z);
|
||||
retval->SetW(transformedPoint.w);
|
||||
} else if (point.mZ != 0 || point.mW != 1.0) {
|
||||
gfx::Matrix4x4 tempMatrix(gfx::Matrix4x4::From2D(*mMatrix2D));
|
||||
gfx::Matrix4x4Double tempMatrix(gfx::Matrix4x4Double::From2D(*mMatrix2D));
|
||||
|
||||
gfx::Point4D transformedPoint;
|
||||
gfx::PointDouble4D transformedPoint;
|
||||
transformedPoint.x = point.mX;
|
||||
transformedPoint.y = point.mY;
|
||||
transformedPoint.z = point.mZ;
|
||||
@ -227,7 +434,7 @@ already_AddRefed<DOMPoint> DOMMatrixReadOnly::TransformPoint(
|
||||
retval->SetZ(transformedPoint.z);
|
||||
retval->SetW(transformedPoint.w);
|
||||
} else {
|
||||
gfx::Point transformedPoint;
|
||||
gfx::PointDouble transformedPoint;
|
||||
transformedPoint.x = point.mX;
|
||||
transformedPoint.y = point.mY;
|
||||
|
||||
@ -289,90 +496,205 @@ void DOMMatrixReadOnly::ToFloat64Array(JSContext* aCx,
|
||||
aResult.set(&value.toObject());
|
||||
}
|
||||
|
||||
// Convenient way to append things as floats, not doubles. We use this because
|
||||
// we only want to output about 6 digits of precision for our matrix()
|
||||
// functions, to preserve the behavior we used to have when we used
|
||||
// AppendPrintf.
|
||||
static void AppendFloat(nsAString& aStr, float f) { aStr.AppendFloat(f); }
|
||||
|
||||
void DOMMatrixReadOnly::Stringify(nsAString& aResult) {
|
||||
void DOMMatrixReadOnly::Stringify(nsAString& aResult, ErrorResult& aRv) {
|
||||
char cbuf[JS::MaximumNumberToStringLength];
|
||||
nsAutoString matrixStr;
|
||||
auto AppendDouble = [&aRv, &cbuf, &matrixStr](double d,
|
||||
bool isLastItem = false) {
|
||||
if (!mozilla::IsFinite(d)) {
|
||||
aRv.ThrowDOMException(
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
"Matrix with a non-finite element cannot be stringified.");
|
||||
return false;
|
||||
}
|
||||
JS::NumberToString(d, cbuf);
|
||||
matrixStr.AppendASCII(cbuf);
|
||||
if (!isLastItem) {
|
||||
matrixStr.AppendLiteral(", ");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (mMatrix3D) {
|
||||
// We can't use AppendPrintf here, because it does locale-specific
|
||||
// formatting of floating-point values.
|
||||
matrixStr.AssignLiteral("matrix3d(");
|
||||
AppendFloat(matrixStr, M11());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M12());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M13());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M14());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M21());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M22());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M23());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M24());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M31());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M32());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M33());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M34());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M41());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M42());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M43());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, M44());
|
||||
if (!AppendDouble(M11()) || !AppendDouble(M12()) || !AppendDouble(M13()) ||
|
||||
!AppendDouble(M14()) || !AppendDouble(M21()) || !AppendDouble(M22()) ||
|
||||
!AppendDouble(M23()) || !AppendDouble(M24()) || !AppendDouble(M31()) ||
|
||||
!AppendDouble(M32()) || !AppendDouble(M33()) || !AppendDouble(M34()) ||
|
||||
!AppendDouble(M41()) || !AppendDouble(M42()) || !AppendDouble(M43()) ||
|
||||
!AppendDouble(M44(), true)) {
|
||||
return;
|
||||
}
|
||||
matrixStr.AppendLiteral(")");
|
||||
} else {
|
||||
// We can't use AppendPrintf here, because it does locale-specific
|
||||
// formatting of floating-point values.
|
||||
matrixStr.AssignLiteral("matrix(");
|
||||
AppendFloat(matrixStr, A());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, B());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, C());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, D());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, E());
|
||||
matrixStr.AppendLiteral(", ");
|
||||
AppendFloat(matrixStr, F());
|
||||
if (!AppendDouble(A()) || !AppendDouble(B()) || !AppendDouble(C()) ||
|
||||
!AppendDouble(D()) || !AppendDouble(E()) || !AppendDouble(F(), true)) {
|
||||
return;
|
||||
}
|
||||
matrixStr.AppendLiteral(")");
|
||||
}
|
||||
|
||||
aResult = matrixStr;
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(const GlobalObject& aGlobal,
|
||||
// https://drafts.fxtf.org/geometry/#structured-serialization
|
||||
bool DOMMatrixReadOnly::WriteStructuredClone(
|
||||
JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
|
||||
#define WriteDouble(d) \
|
||||
JS_WriteUint32Pair(aWriter, (BitwiseCast<uint64_t>(d) >> 32) & 0xffffffff, \
|
||||
BitwiseCast<uint64_t>(d) & 0xffffffff)
|
||||
|
||||
const uint8_t is2D = Is2D();
|
||||
|
||||
if (!JS_WriteBytes(aWriter, &is2D, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is2D == 1) {
|
||||
return WriteDouble(mMatrix2D->_11) && WriteDouble(mMatrix2D->_12) &&
|
||||
WriteDouble(mMatrix2D->_21) && WriteDouble(mMatrix2D->_22) &&
|
||||
WriteDouble(mMatrix2D->_31) && WriteDouble(mMatrix2D->_32);
|
||||
}
|
||||
|
||||
return WriteDouble(mMatrix3D->_11) && WriteDouble(mMatrix3D->_12) &&
|
||||
WriteDouble(mMatrix3D->_13) && WriteDouble(mMatrix3D->_14) &&
|
||||
WriteDouble(mMatrix3D->_21) && WriteDouble(mMatrix3D->_22) &&
|
||||
WriteDouble(mMatrix3D->_23) && WriteDouble(mMatrix3D->_24) &&
|
||||
WriteDouble(mMatrix3D->_31) && WriteDouble(mMatrix3D->_32) &&
|
||||
WriteDouble(mMatrix3D->_33) && WriteDouble(mMatrix3D->_34) &&
|
||||
WriteDouble(mMatrix3D->_41) && WriteDouble(mMatrix3D->_42) &&
|
||||
WriteDouble(mMatrix3D->_43) && WriteDouble(mMatrix3D->_44);
|
||||
|
||||
#undef WriteDouble
|
||||
}
|
||||
|
||||
bool DOMMatrixReadOnly::ReadStructuredCloneElements(
|
||||
JSStructuredCloneReader* aReader, DOMMatrixReadOnly* matrix) {
|
||||
uint32_t high;
|
||||
uint32_t low;
|
||||
|
||||
#define ReadDouble(d) \
|
||||
if (!JS_ReadUint32Pair(aReader, &high, &low)) { \
|
||||
return false; \
|
||||
} \
|
||||
(*(d) = BitwiseCast<double>(static_cast<uint64_t>(high) << 32 | low))
|
||||
|
||||
if (matrix->Is2D() == 1) {
|
||||
ReadDouble(&(matrix->mMatrix2D->_11));
|
||||
ReadDouble(&(matrix->mMatrix2D->_12));
|
||||
ReadDouble(&(matrix->mMatrix2D->_21));
|
||||
ReadDouble(&(matrix->mMatrix2D->_22));
|
||||
ReadDouble(&(matrix->mMatrix2D->_31));
|
||||
ReadDouble(&(matrix->mMatrix2D->_32));
|
||||
} else {
|
||||
ReadDouble(&(matrix->mMatrix3D->_11));
|
||||
ReadDouble(&(matrix->mMatrix3D->_12));
|
||||
ReadDouble(&(matrix->mMatrix3D->_13));
|
||||
ReadDouble(&(matrix->mMatrix3D->_14));
|
||||
ReadDouble(&(matrix->mMatrix3D->_21));
|
||||
ReadDouble(&(matrix->mMatrix3D->_22));
|
||||
ReadDouble(&(matrix->mMatrix3D->_23));
|
||||
ReadDouble(&(matrix->mMatrix3D->_24));
|
||||
ReadDouble(&(matrix->mMatrix3D->_31));
|
||||
ReadDouble(&(matrix->mMatrix3D->_32));
|
||||
ReadDouble(&(matrix->mMatrix3D->_33));
|
||||
ReadDouble(&(matrix->mMatrix3D->_34));
|
||||
ReadDouble(&(matrix->mMatrix3D->_41));
|
||||
ReadDouble(&(matrix->mMatrix3D->_42));
|
||||
ReadDouble(&(matrix->mMatrix3D->_43));
|
||||
ReadDouble(&(matrix->mMatrix3D->_44));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
#undef ReadDouble
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromMatrix(
|
||||
nsISupports* aParent, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv) {
|
||||
DOMMatrixInit matrixInit(aMatrixInit);
|
||||
if (!ValidateAndFixupMatrixInit(matrixInit, aRv)) {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
RefPtr<DOMMatrix> matrix = new DOMMatrix(aParent, matrixInit.mIs2D.Value());
|
||||
matrix->SetDataFromMatrixInit(matrixInit);
|
||||
return matrix.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromMatrix(
|
||||
const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
|
||||
RefPtr<DOMMatrix> matrix =
|
||||
FromMatrix(aGlobal.GetAsSupports(), aMatrixInit, aRv);
|
||||
return matrix.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv) {
|
||||
aArray32.ComputeLengthAndData();
|
||||
|
||||
const int length = aArray32.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray32.Data(), length, aRv);
|
||||
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv) {
|
||||
aArray64.ComputeLengthAndData();
|
||||
|
||||
const int length = aArray64.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray64.Data(), length, aRv);
|
||||
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(
|
||||
const GlobalObject& aGlobal, const nsAString& aTransformList,
|
||||
const GlobalObject& aGlobal,
|
||||
const Optional<UTF8StringOrUnrestrictedDoubleSequenceOrDOMMatrixReadOnly>&
|
||||
aArg,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
|
||||
obj = obj->SetMatrixValue(aTransformList, aRv);
|
||||
return obj.forget();
|
||||
}
|
||||
if (!aArg.WasPassed()) {
|
||||
RefPtr<DOMMatrix> rval = new DOMMatrix(aGlobal.GetAsSupports());
|
||||
return rval.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(
|
||||
const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), aOther);
|
||||
const auto& arg = aArg.Value();
|
||||
if (arg.IsUTF8String()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> win =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!win) {
|
||||
aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<DOMMatrix> rval = new DOMMatrix(aGlobal.GetAsSupports());
|
||||
rval->SetMatrixValue(arg.GetAsUTF8String(), aRv);
|
||||
return rval.forget();
|
||||
}
|
||||
if (arg.IsDOMMatrixReadOnly()) {
|
||||
RefPtr<DOMMatrix> obj =
|
||||
new DOMMatrix(aGlobal.GetAsSupports(), arg.GetAsDOMMatrixReadOnly());
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
const auto& sequence = arg.GetAsUnrestrictedDoubleSequence();
|
||||
const int length = sequence.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrix> rval = new DOMMatrix(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(rval, sequence.Elements(), length, aRv);
|
||||
return rval.forget();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -409,76 +731,77 @@ static void SetDataInMatrix(DOMMatrixReadOnly* aMatrix, const T* aData,
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(const GlobalObject& aGlobal,
|
||||
const Float32Array& aArray32,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
|
||||
aArray32.ComputeLengthAndData();
|
||||
SetDataInMatrix(obj, aArray32.Data(), aArray32.Length(), aRv);
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader) {
|
||||
uint8_t is2D;
|
||||
|
||||
return obj.forget();
|
||||
}
|
||||
if (!JS_ReadBytes(aReader, &is2D, 1)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(const GlobalObject& aGlobal,
|
||||
const Float64Array& aArray64,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
|
||||
aArray64.ComputeLengthAndData();
|
||||
SetDataInMatrix(obj, aArray64.Data(), aArray64.Length(), aRv);
|
||||
RefPtr<DOMMatrix> rval = new DOMMatrix(aGlobal, is2D);
|
||||
|
||||
return obj.forget();
|
||||
}
|
||||
if (!ReadStructuredCloneElements(aReader, rval)) {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(
|
||||
const GlobalObject& aGlobal, const Sequence<double>& aNumberSequence,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
|
||||
SetDataInMatrix(obj, aNumberSequence.Elements(), aNumberSequence.Length(),
|
||||
aRv);
|
||||
|
||||
return obj.forget();
|
||||
return rval.forget();
|
||||
}
|
||||
|
||||
void DOMMatrixReadOnly::Ensure3DMatrix() {
|
||||
if (!mMatrix3D) {
|
||||
mMatrix3D = new gfx::Matrix4x4(gfx::Matrix4x4::From2D(*mMatrix2D));
|
||||
mMatrix3D =
|
||||
new gfx::Matrix4x4Double(gfx::Matrix4x4Double::From2D(*mMatrix2D));
|
||||
mMatrix2D = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::MultiplySelf(const DOMMatrix& aOther) {
|
||||
if (aOther.IsIdentity()) {
|
||||
DOMMatrix* DOMMatrix::MultiplySelf(const DOMMatrixInit& aOtherInit,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> other = FromMatrix(mParent, aOtherInit, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(other);
|
||||
if (other->IsIdentity()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (aOther.Is2D()) {
|
||||
if (other->Is2D()) {
|
||||
if (mMatrix3D) {
|
||||
*mMatrix3D = gfx::Matrix4x4::From2D(*aOther.mMatrix2D) * *mMatrix3D;
|
||||
*mMatrix3D = gfx::Matrix4x4Double::From2D(*other->mMatrix2D) * *mMatrix3D;
|
||||
} else {
|
||||
*mMatrix2D = *aOther.mMatrix2D * *mMatrix2D;
|
||||
*mMatrix2D = *other->mMatrix2D * *mMatrix2D;
|
||||
}
|
||||
} else {
|
||||
Ensure3DMatrix();
|
||||
*mMatrix3D = *aOther.mMatrix3D * *mMatrix3D;
|
||||
*mMatrix3D = *other->mMatrix3D * *mMatrix3D;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::PreMultiplySelf(const DOMMatrix& aOther) {
|
||||
if (aOther.IsIdentity()) {
|
||||
DOMMatrix* DOMMatrix::PreMultiplySelf(const DOMMatrixInit& aOtherInit,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrix> other = FromMatrix(mParent, aOtherInit, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(other);
|
||||
if (other->IsIdentity()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (aOther.Is2D()) {
|
||||
if (other->Is2D()) {
|
||||
if (mMatrix3D) {
|
||||
*mMatrix3D = *mMatrix3D * gfx::Matrix4x4::From2D(*aOther.mMatrix2D);
|
||||
*mMatrix3D = *mMatrix3D * gfx::Matrix4x4Double::From2D(*other->mMatrix2D);
|
||||
} else {
|
||||
*mMatrix2D = *mMatrix2D * *aOther.mMatrix2D;
|
||||
*mMatrix2D = *mMatrix2D * *other->mMatrix2D;
|
||||
}
|
||||
} else {
|
||||
Ensure3DMatrix();
|
||||
*mMatrix3D = *mMatrix3D * *aOther.mMatrix3D;
|
||||
*mMatrix3D = *mMatrix3D * *other->mMatrix3D;
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -499,40 +822,24 @@ DOMMatrix* DOMMatrix::TranslateSelf(double aTx, double aTy, double aTz) {
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::ScaleSelf(double aScale, double aOriginX,
|
||||
double aOriginY) {
|
||||
ScaleNonUniformSelf(aScale, aScale, 1.0, aOriginX, aOriginY, 0);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::Scale3dSelf(double aScale, double aOriginX,
|
||||
double aOriginY, double aOriginZ) {
|
||||
ScaleNonUniformSelf(aScale, aScale, aScale, aOriginX, aOriginY, aOriginZ);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::ScaleNonUniformSelf(double aScaleX, double aScaleY,
|
||||
DOMMatrix* DOMMatrix::ScaleSelf(double aScaleX, const Optional<double>& aScaleY,
|
||||
double aScaleZ, double aOriginX,
|
||||
double aOriginY, double aOriginZ) {
|
||||
if (aScaleX == 1.0 && aScaleY == 1.0 && aScaleZ == 1.0) {
|
||||
return this;
|
||||
}
|
||||
const double scaleY = aScaleY.WasPassed() ? aScaleY.Value() : aScaleX;
|
||||
|
||||
TranslateSelf(aOriginX, aOriginY, aOriginZ);
|
||||
|
||||
if (mMatrix3D || aScaleZ != 1.0 || aOriginZ != 0) {
|
||||
if (mMatrix3D || aScaleZ != 1.0) {
|
||||
Ensure3DMatrix();
|
||||
gfx::Matrix4x4 m;
|
||||
gfx::Matrix4x4Double m;
|
||||
m._11 = aScaleX;
|
||||
m._22 = aScaleY;
|
||||
m._22 = scaleY;
|
||||
m._33 = aScaleZ;
|
||||
*mMatrix3D = m * *mMatrix3D;
|
||||
} else {
|
||||
gfx::Matrix m;
|
||||
gfx::MatrixDouble m;
|
||||
m._11 = aScaleX;
|
||||
m._22 = aScaleY;
|
||||
m._22 = scaleY;
|
||||
*mMatrix2D = m * *mMatrix2D;
|
||||
}
|
||||
|
||||
@ -541,31 +848,60 @@ DOMMatrix* DOMMatrix::ScaleNonUniformSelf(double aScaleX, double aScaleY,
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::RotateFromVectorSelf(double aX, double aY) {
|
||||
if (aX == 0.0 || aY == 0.0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
RotateSelf(atan2(aY, aX) / radPerDegree);
|
||||
DOMMatrix* DOMMatrix::Scale3dSelf(double aScale, double aOriginX,
|
||||
double aOriginY, double aOriginZ) {
|
||||
ScaleSelf(aScale, Optional<double>(aScale), aScale, aOriginX, aOriginY,
|
||||
aOriginZ);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::RotateSelf(double aAngle, double aOriginX,
|
||||
double aOriginY) {
|
||||
if (fmod(aAngle, 360) == 0) {
|
||||
DOMMatrix* DOMMatrix::RotateFromVectorSelf(double aX, double aY) {
|
||||
const double angle = (aX == 0.0 && aY == 0.0) ? 0 : atan2(aY, aX);
|
||||
|
||||
if (fmod(angle, 2 * M_PI) == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
TranslateSelf(aOriginX, aOriginY);
|
||||
|
||||
if (mMatrix3D) {
|
||||
RotateAxisAngleSelf(0, 0, 1, aAngle);
|
||||
RotateAxisAngleSelf(0, 0, 1, angle / radPerDegree);
|
||||
} else {
|
||||
*mMatrix2D = mMatrix2D->PreRotate(aAngle * radPerDegree);
|
||||
*mMatrix2D = mMatrix2D->PreRotate(angle);
|
||||
}
|
||||
|
||||
TranslateSelf(-aOriginX, -aOriginY);
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::RotateSelf(double aRotX, const Optional<double>& aRotY,
|
||||
const Optional<double>& aRotZ) {
|
||||
double rotY;
|
||||
double rotZ;
|
||||
if (!aRotY.WasPassed() && !aRotZ.WasPassed()) {
|
||||
rotZ = aRotX;
|
||||
aRotX = 0;
|
||||
rotY = 0;
|
||||
} else {
|
||||
rotY = aRotY.WasPassed() ? aRotY.Value() : 0;
|
||||
rotZ = aRotZ.WasPassed() ? aRotZ.Value() : 0;
|
||||
}
|
||||
|
||||
if (aRotX != 0 || rotY != 0) {
|
||||
Ensure3DMatrix();
|
||||
}
|
||||
|
||||
if (mMatrix3D) {
|
||||
if (fmod(rotZ, 360) != 0) {
|
||||
mMatrix3D->RotateZ(rotZ * radPerDegree);
|
||||
}
|
||||
if (fmod(rotY, 360) != 0) {
|
||||
mMatrix3D->RotateY(rotY * radPerDegree);
|
||||
}
|
||||
if (fmod(aRotX, 360) != 0) {
|
||||
mMatrix3D->RotateX(aRotX * radPerDegree);
|
||||
}
|
||||
} else if (fmod(rotZ, 360) != 0) {
|
||||
*mMatrix2D = mMatrix2D->PreRotate(rotZ * radPerDegree);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -579,7 +915,7 @@ DOMMatrix* DOMMatrix::RotateAxisAngleSelf(double aX, double aY, double aZ,
|
||||
aAngle *= radPerDegree;
|
||||
|
||||
Ensure3DMatrix();
|
||||
gfx::Matrix4x4 m;
|
||||
gfx::Matrix4x4Double m;
|
||||
m.SetRotateAxisAngle(aX, aY, aZ, aAngle);
|
||||
|
||||
*mMatrix3D = m * *mMatrix3D;
|
||||
@ -593,11 +929,11 @@ DOMMatrix* DOMMatrix::SkewXSelf(double aSx) {
|
||||
}
|
||||
|
||||
if (mMatrix3D) {
|
||||
gfx::Matrix4x4 m;
|
||||
gfx::Matrix4x4Double m;
|
||||
m._21 = tan(aSx * radPerDegree);
|
||||
*mMatrix3D = m * *mMatrix3D;
|
||||
} else {
|
||||
gfx::Matrix m;
|
||||
gfx::MatrixDouble m;
|
||||
m._21 = tan(aSx * radPerDegree);
|
||||
*mMatrix2D = m * *mMatrix2D;
|
||||
}
|
||||
@ -611,11 +947,11 @@ DOMMatrix* DOMMatrix::SkewYSelf(double aSy) {
|
||||
}
|
||||
|
||||
if (mMatrix3D) {
|
||||
gfx::Matrix4x4 m;
|
||||
gfx::Matrix4x4Double m;
|
||||
m._12 = tan(aSy * radPerDegree);
|
||||
*mMatrix3D = m * *mMatrix3D;
|
||||
} else {
|
||||
gfx::Matrix m;
|
||||
gfx::MatrixDouble m;
|
||||
m._12 = tan(aSy * radPerDegree);
|
||||
*mMatrix2D = m * *mMatrix2D;
|
||||
}
|
||||
@ -631,7 +967,7 @@ DOMMatrix* DOMMatrix::InvertSelf() {
|
||||
} else if (!mMatrix2D->Invert()) {
|
||||
mMatrix2D = nullptr;
|
||||
|
||||
mMatrix3D = new gfx::Matrix4x4();
|
||||
mMatrix3D = new gfx::Matrix4x4Double();
|
||||
mMatrix3D->SetNAN();
|
||||
}
|
||||
|
||||
@ -639,7 +975,7 @@ DOMMatrix* DOMMatrix::InvertSelf() {
|
||||
}
|
||||
|
||||
DOMMatrixReadOnly* DOMMatrixReadOnly::SetMatrixValue(
|
||||
const nsAString& aTransformList, ErrorResult& aRv) {
|
||||
const nsACString& aTransformList, ErrorResult& aRv) {
|
||||
// An empty string is a no-op.
|
||||
if (aTransformList.IsEmpty()) {
|
||||
return this;
|
||||
@ -655,7 +991,9 @@ DOMMatrixReadOnly* DOMMatrixReadOnly::SetMatrixValue(
|
||||
|
||||
if (!contains3dTransform) {
|
||||
mMatrix3D = nullptr;
|
||||
mMatrix2D = new gfx::Matrix();
|
||||
if (!mMatrix2D) {
|
||||
mMatrix2D = new gfx::MatrixDouble();
|
||||
}
|
||||
|
||||
SetA(transform._11);
|
||||
SetB(transform._12);
|
||||
@ -664,14 +1002,14 @@ DOMMatrixReadOnly* DOMMatrixReadOnly::SetMatrixValue(
|
||||
SetE(transform._41);
|
||||
SetF(transform._42);
|
||||
} else {
|
||||
mMatrix3D = new gfx::Matrix4x4(transform);
|
||||
mMatrix3D = new gfx::Matrix4x4Double(transform);
|
||||
mMatrix2D = nullptr;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DOMMatrix* DOMMatrix::SetMatrixValue(const nsAString& aTransformList,
|
||||
DOMMatrix* DOMMatrix::SetMatrixValue(const nsACString& aTransformList,
|
||||
ErrorResult& aRv) {
|
||||
DOMMatrixReadOnly::SetMatrixValue(aTransformList, aRv);
|
||||
return this;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef MOZILLA_DOM_DOMMATRIX_H_
|
||||
#define MOZILLA_DOM_DOMMATRIX_H_
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
@ -13,7 +14,9 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4Double
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -21,26 +24,33 @@ namespace dom {
|
||||
class GlobalObject;
|
||||
class DOMMatrix;
|
||||
class DOMPoint;
|
||||
class StringOrUnrestrictedDoubleSequence;
|
||||
class UTF8StringOrUnrestrictedDoubleSequenceOrDOMMatrixReadOnly;
|
||||
struct DOMPointInit;
|
||||
struct DOMMatrixInit;
|
||||
struct DOMMatrix2DInit;
|
||||
|
||||
class DOMMatrixReadOnly : public nsWrapperCache {
|
||||
public:
|
||||
explicit DOMMatrixReadOnly(nsISupports* aParent)
|
||||
: mParent(aParent), mMatrix2D(new gfx::Matrix()) {}
|
||||
: mParent(aParent), mMatrix2D(new gfx::MatrixDouble()) {}
|
||||
|
||||
DOMMatrixReadOnly(nsISupports* aParent, const DOMMatrixReadOnly& other)
|
||||
: mParent(aParent) {
|
||||
if (other.mMatrix2D) {
|
||||
mMatrix2D = new gfx::Matrix(*other.mMatrix2D);
|
||||
mMatrix2D = new gfx::MatrixDouble(*other.mMatrix2D);
|
||||
} else {
|
||||
mMatrix3D = new gfx::Matrix4x4(*other.mMatrix3D);
|
||||
mMatrix3D = new gfx::Matrix4x4Double(*other.mMatrix3D);
|
||||
}
|
||||
}
|
||||
|
||||
DOMMatrixReadOnly(nsISupports* aParent, const gfx::Matrix4x4& aMatrix)
|
||||
: mParent(aParent) {
|
||||
mMatrix3D = new gfx::Matrix4x4(aMatrix);
|
||||
mMatrix3D = new gfx::Matrix4x4Double(aMatrix);
|
||||
}
|
||||
|
||||
DOMMatrixReadOnly(nsISupports* aParent, const gfx::Matrix& aMatrix)
|
||||
: mParent(aParent) {
|
||||
mMatrix2D = new gfx::MatrixDouble(aMatrix);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMMatrixReadOnly)
|
||||
@ -50,11 +60,35 @@ class DOMMatrixReadOnly : public nsWrapperCache {
|
||||
virtual JSObject* WrapObject(JSContext* cx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<DOMMatrixReadOnly> FromMatrix(
|
||||
nsISupports* aParent, const DOMMatrix2DInit& aMatrixInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrixReadOnly> FromMatrix(
|
||||
nsISupports* aParent, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrixReadOnly> FromMatrix(
|
||||
const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrixReadOnly> FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrixReadOnly> FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrixReadOnly> Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
const Optional<StringOrUnrestrictedDoubleSequence>& aArg,
|
||||
const Optional<UTF8StringOrUnrestrictedDoubleSequenceOrDOMMatrixReadOnly>&
|
||||
aArg,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrixReadOnly> ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader);
|
||||
|
||||
// clang-format off
|
||||
#define GetMatrixMember(entry2D, entry3D, default) \
|
||||
{ \
|
||||
@ -148,22 +182,25 @@ class DOMMatrixReadOnly : public nsWrapperCache {
|
||||
|
||||
already_AddRefed<DOMMatrix> Translate(double aTx, double aTy,
|
||||
double aTz = 0) const;
|
||||
already_AddRefed<DOMMatrix> Scale(double aScale, double aOriginX = 0,
|
||||
double aOriginY = 0) const;
|
||||
already_AddRefed<DOMMatrix> Scale(double aScaleX,
|
||||
const Optional<double>& aScaleY,
|
||||
double aScaleZ, double aOriginX,
|
||||
double aOriginY, double aOriginZ) const;
|
||||
already_AddRefed<DOMMatrix> Scale3d(double aScale, double aOriginX = 0,
|
||||
double aOriginY = 0,
|
||||
double aOriginZ = 0) const;
|
||||
already_AddRefed<DOMMatrix> ScaleNonUniform(
|
||||
double aScaleX, double aScaleY = 1.0, double aScaleZ = 1.0,
|
||||
double aOriginX = 0, double aOriginY = 0, double aOriginZ = 0) const;
|
||||
already_AddRefed<DOMMatrix> Rotate(double aAngle, double aOriginX = 0,
|
||||
double aOriginY = 0) const;
|
||||
already_AddRefed<DOMMatrix> ScaleNonUniform(double aScaleX,
|
||||
double aScaleY) const;
|
||||
already_AddRefed<DOMMatrix> Rotate(double aRotX,
|
||||
const Optional<double>& aRotY,
|
||||
const Optional<double>& aRotZ) const;
|
||||
already_AddRefed<DOMMatrix> RotateFromVector(double aX, double aY) const;
|
||||
already_AddRefed<DOMMatrix> RotateAxisAngle(double aX, double aY, double aZ,
|
||||
double aAngle) const;
|
||||
already_AddRefed<DOMMatrix> SkewX(double aSx) const;
|
||||
already_AddRefed<DOMMatrix> SkewY(double aSy) const;
|
||||
already_AddRefed<DOMMatrix> Multiply(const DOMMatrix& aOther) const;
|
||||
already_AddRefed<DOMMatrix> Multiply(const DOMMatrixInit& aOther,
|
||||
ErrorResult& aRv) const;
|
||||
already_AddRefed<DOMMatrix> FlipX() const;
|
||||
already_AddRefed<DOMMatrix> FlipY() const;
|
||||
already_AddRefed<DOMMatrix> Inverse() const;
|
||||
@ -175,19 +212,46 @@ class DOMMatrixReadOnly : public nsWrapperCache {
|
||||
ErrorResult& aRv) const;
|
||||
void ToFloat64Array(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
|
||||
ErrorResult& aRv) const;
|
||||
void Stringify(nsAString& aResult);
|
||||
void Stringify(nsAString& aResult, ErrorResult& aRv);
|
||||
|
||||
bool WriteStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter) const;
|
||||
const gfx::MatrixDouble* GetInternal2D() const {
|
||||
if (Is2D()) {
|
||||
return mMatrix2D;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
nsAutoPtr<gfx::Matrix> mMatrix2D;
|
||||
nsAutoPtr<gfx::Matrix4x4> mMatrix3D;
|
||||
nsAutoPtr<gfx::MatrixDouble> mMatrix2D;
|
||||
nsAutoPtr<gfx::Matrix4x4Double> mMatrix3D;
|
||||
|
||||
virtual ~DOMMatrixReadOnly() {}
|
||||
|
||||
DOMMatrixReadOnly* SetMatrixValue(const nsAString& aTransformList,
|
||||
ErrorResult& aRv);
|
||||
/**
|
||||
* Sets data from a fully validated and fixed-up matrix init,
|
||||
* where all of its members are properly defined.
|
||||
* The init dictionary's dimension must match the matrix one.
|
||||
*/
|
||||
void SetDataFromMatrix2DInit(const DOMMatrix2DInit& aMatrixInit);
|
||||
void SetDataFromMatrixInit(const DOMMatrixInit& aMatrixInit);
|
||||
|
||||
DOMMatrixReadOnly* SetMatrixValue(const nsACString&, ErrorResult&);
|
||||
void Ensure3DMatrix();
|
||||
|
||||
DOMMatrixReadOnly(nsISupports* aParent, bool is2D) : mParent(aParent) {
|
||||
if (is2D) {
|
||||
mMatrix2D = new gfx::MatrixDouble();
|
||||
} else {
|
||||
mMatrix3D = new gfx::Matrix4x4Double();
|
||||
}
|
||||
}
|
||||
|
||||
static bool ReadStructuredCloneElements(JSStructuredCloneReader* aReader,
|
||||
DOMMatrixReadOnly* matrix);
|
||||
|
||||
private:
|
||||
DOMMatrixReadOnly() = delete;
|
||||
DOMMatrixReadOnly(const DOMMatrixReadOnly&) = delete;
|
||||
@ -204,47 +268,60 @@ class DOMMatrix : public DOMMatrixReadOnly {
|
||||
DOMMatrix(nsISupports* aParent, const gfx::Matrix4x4& aMatrix)
|
||||
: DOMMatrixReadOnly(aParent, aMatrix) {}
|
||||
|
||||
static already_AddRefed<DOMMatrix> Constructor(const GlobalObject& aGlobal,
|
||||
DOMMatrix(nsISupports* aParent, const gfx::Matrix& aMatrix)
|
||||
: DOMMatrixReadOnly(aParent, aMatrix) {}
|
||||
|
||||
static already_AddRefed<DOMMatrix> FromMatrix(
|
||||
nsISupports* aParent, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrix> FromMatrix(
|
||||
const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrix> FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrix> FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrix> Constructor(
|
||||
const GlobalObject& aGlobal, const nsAString& aTransformList,
|
||||
ErrorResult& aRv);
|
||||
static already_AddRefed<DOMMatrix> Constructor(
|
||||
const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther,
|
||||
ErrorResult& aRv);
|
||||
static already_AddRefed<DOMMatrix> Constructor(const GlobalObject& aGlobal,
|
||||
const Float32Array& aArray32,
|
||||
ErrorResult& aRv);
|
||||
static already_AddRefed<DOMMatrix> Constructor(const GlobalObject& aGlobal,
|
||||
const Float64Array& aArray64,
|
||||
ErrorResult& aRv);
|
||||
static already_AddRefed<DOMMatrix> Constructor(
|
||||
const GlobalObject& aGlobal, const Sequence<double>& aNumberSequence,
|
||||
const GlobalObject& aGlobal,
|
||||
const Optional<UTF8StringOrUnrestrictedDoubleSequenceOrDOMMatrixReadOnly>&
|
||||
aArg,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<DOMMatrix> ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
DOMMatrix* MultiplySelf(const DOMMatrix& aOther);
|
||||
DOMMatrix* PreMultiplySelf(const DOMMatrix& aOther);
|
||||
DOMMatrix* MultiplySelf(const DOMMatrixInit& aOther, ErrorResult& aRv);
|
||||
DOMMatrix* PreMultiplySelf(const DOMMatrixInit& aOther, ErrorResult& aRv);
|
||||
DOMMatrix* TranslateSelf(double aTx, double aTy, double aTz = 0);
|
||||
DOMMatrix* ScaleSelf(double aScale, double aOriginX = 0, double aOriginY = 0);
|
||||
DOMMatrix* ScaleSelf(double aScaleX, const Optional<double>& aScaleY,
|
||||
double aScaleZ, double aOriginX, double aOriginY,
|
||||
double aOriginZ);
|
||||
DOMMatrix* Scale3dSelf(double aScale, double aOriginX = 0,
|
||||
double aOriginY = 0, double aOriginZ = 0);
|
||||
DOMMatrix* ScaleNonUniformSelf(double aScaleX, double aScaleY = 1,
|
||||
double aScaleZ = 1, double aOriginX = 0,
|
||||
double aOriginY = 0, double aOriginZ = 0);
|
||||
DOMMatrix* RotateSelf(double aAngle, double aOriginX = 0,
|
||||
double aOriginY = 0);
|
||||
DOMMatrix* RotateSelf(double aRotX, const Optional<double>& aRotY,
|
||||
const Optional<double>& aRotZ);
|
||||
DOMMatrix* RotateFromVectorSelf(double aX, double aY);
|
||||
DOMMatrix* RotateAxisAngleSelf(double aX, double aY, double aZ,
|
||||
double aAngle);
|
||||
DOMMatrix* SkewXSelf(double aSx);
|
||||
DOMMatrix* SkewYSelf(double aSy);
|
||||
DOMMatrix* InvertSelf();
|
||||
DOMMatrix* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv);
|
||||
DOMMatrix* SetMatrixValue(const nsACString&, ErrorResult&);
|
||||
|
||||
virtual ~DOMMatrix() {}
|
||||
|
||||
private:
|
||||
DOMMatrix(nsISupports* aParent, bool is2D)
|
||||
: DOMMatrixReadOnly(aParent, is2D) {}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -7,16 +7,16 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "SystemPrincipal.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/NullPrincipal.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
@ -31,7 +31,8 @@ DOMParser::DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal,
|
||||
mPrincipal(aDocPrincipal),
|
||||
mDocumentURI(aDocumentURI),
|
||||
mBaseURI(aBaseURI),
|
||||
mForceEnableXULXBL(false) {
|
||||
mForceEnableXULXBL(false),
|
||||
mForceEnableDTD(false) {
|
||||
MOZ_ASSERT(aDocPrincipal);
|
||||
MOZ_ASSERT(aDocumentURI);
|
||||
}
|
||||
@ -49,10 +50,6 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser, mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser)
|
||||
|
||||
static const char* StringFromSupportedType(SupportedType aType) {
|
||||
return SupportedTypeValues::strings[static_cast<int>(aType)].value;
|
||||
}
|
||||
|
||||
already_AddRefed<Document> DOMParser::ParseFromString(const nsAString& aStr,
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv) {
|
||||
@ -67,6 +64,10 @@ already_AddRefed<Document> DOMParser::ParseFromString(const nsAString& aStr,
|
||||
document->ForceEnableXULXBL();
|
||||
}
|
||||
|
||||
if (mForceEnableDTD) {
|
||||
document->ForceSkipDTDSecurityChecks();
|
||||
}
|
||||
|
||||
nsresult rv = nsContentUtils::ParseDocumentHTML(aStr, document, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
@ -96,6 +97,22 @@ already_AddRefed<Document> DOMParser::ParseFromString(const nsAString& aStr,
|
||||
aType, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Document> DOMParser::ParseFromSafeString(const nsAString& aStr,
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv) {
|
||||
// Since we disable cross docGroup node adoption, it is safe to create
|
||||
// new document with the system principal, then the new document will be
|
||||
// placed in the same docGroup as the chrome document.
|
||||
nsCOMPtr<nsIPrincipal> docPrincipal = mPrincipal;
|
||||
if (!mPrincipal->IsSystemPrincipal()) {
|
||||
mPrincipal = SystemPrincipal::Create();
|
||||
}
|
||||
|
||||
RefPtr<Document> ret = ParseFromString(aStr, aType, aRv);
|
||||
mPrincipal = docPrincipal;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv) {
|
||||
@ -159,11 +176,12 @@ already_AddRefed<Document> DOMParser::ParseFromStream(nsIInputStream* aStream,
|
||||
|
||||
// Create a fake channel
|
||||
nsCOMPtr<nsIChannel> parserChannel;
|
||||
NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI,
|
||||
NS_NewInputStreamChannel(
|
||||
getter_AddRefs(parserChannel), mDocumentURI,
|
||||
nullptr, // aStream
|
||||
mPrincipal, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nsDependentCString(StringFromSupportedType(aType)));
|
||||
nsDependentCSubstring(SupportedTypeValues::GetString(aType)));
|
||||
if (NS_WARN_IF(!parserChannel)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
@ -181,6 +199,10 @@ already_AddRefed<Document> DOMParser::ParseFromStream(nsIInputStream* aStream,
|
||||
document->ForceEnableXULXBL();
|
||||
}
|
||||
|
||||
if (mForceEnableDTD) {
|
||||
document->ForceSkipDTDSecurityChecks();
|
||||
}
|
||||
|
||||
// Have to pass false for reset here, else the reset will remove
|
||||
// our event listener. Should that listener addition move to later
|
||||
// than this call?
|
||||
@ -225,7 +247,7 @@ already_AddRefed<DOMParser> DOMParser::Constructor(const GlobalObject& aOwner,
|
||||
nsCOMPtr<nsIPrincipal> docPrincipal = aOwner.GetSubjectPrincipal();
|
||||
nsCOMPtr<nsIURI> documentURI;
|
||||
nsIURI* baseURI = nullptr;
|
||||
if (nsContentUtils::IsSystemPrincipal(docPrincipal)) {
|
||||
if (docPrincipal->IsSystemPrincipal()) {
|
||||
docPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
|
||||
docPrincipal->GetURI(getter_AddRefs(documentURI));
|
||||
} else {
|
||||
|
@ -35,6 +35,10 @@ class DOMParser final : public nsISupports, public nsWrapperCache {
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// ChromeOnly API
|
||||
already_AddRefed<Document> ParseFromSafeString(const nsAString& aStr,
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv);
|
||||
// Sequence converts to Span, so we can use this overload for both
|
||||
// the Sequence case and our internal uses.
|
||||
already_AddRefed<Document> ParseFromBuffer(Span<const uint8_t> aBuf,
|
||||
@ -51,7 +55,12 @@ class DOMParser final : public nsISupports, public nsWrapperCache {
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void ForceEnableXULXBL() { mForceEnableXULXBL = true; }
|
||||
void ForceEnableXULXBL() {
|
||||
mForceEnableXULXBL = true;
|
||||
ForceEnableDTD();
|
||||
}
|
||||
|
||||
void ForceEnableDTD() { mForceEnableDTD = true; }
|
||||
|
||||
nsIGlobalObject* GetParentObject() const { return mOwner; }
|
||||
|
||||
@ -76,6 +85,7 @@ class DOMParser final : public nsISupports, public nsWrapperCache {
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
|
||||
bool mForceEnableXULXBL;
|
||||
bool mForceEnableDTD;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -23,8 +23,7 @@ already_AddRefed<DOMPointReadOnly> DOMPointReadOnly::FromPoint(
|
||||
}
|
||||
|
||||
already_AddRefed<DOMPointReadOnly> DOMPointReadOnly::Constructor(
|
||||
const GlobalObject& aGlobal, double aX, double aY, double aZ, double aW,
|
||||
ErrorResult& aRV) {
|
||||
const GlobalObject& aGlobal, double aX, double aY, double aZ, double aW) {
|
||||
RefPtr<DOMPointReadOnly> obj =
|
||||
new DOMPointReadOnly(aGlobal.GetAsSupports(), aX, aY, aZ, aW);
|
||||
return obj.forget();
|
||||
@ -35,6 +34,66 @@ JSObject* DOMPointReadOnly::WrapObject(JSContext* aCx,
|
||||
return DOMPointReadOnly_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMPoint> DOMPointReadOnly::MatrixTransform(
|
||||
const DOMMatrixInit& aInit, ErrorResult& aRv) {
|
||||
RefPtr<DOMMatrixReadOnly> matrix =
|
||||
DOMMatrixReadOnly::FromMatrix(mParent, aInit, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
DOMPointInit init;
|
||||
init.mX = this->mX;
|
||||
init.mY = this->mY;
|
||||
init.mZ = this->mZ;
|
||||
init.mW = this->mW;
|
||||
RefPtr<DOMPoint> point = matrix->TransformPoint(init);
|
||||
return point.forget();
|
||||
}
|
||||
|
||||
// https://drafts.fxtf.org/geometry/#structured-serialization
|
||||
bool DOMPointReadOnly::WriteStructuredClone(
|
||||
JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
|
||||
#define WriteDouble(d) \
|
||||
JS_WriteUint32Pair(aWriter, (BitwiseCast<uint64_t>(d) >> 32) & 0xffffffff, \
|
||||
BitwiseCast<uint64_t>(d) & 0xffffffff)
|
||||
|
||||
return WriteDouble(mX) && WriteDouble(mY) && WriteDouble(mZ) &&
|
||||
WriteDouble(mW);
|
||||
|
||||
#undef WriteDouble
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DOMPointReadOnly> DOMPointReadOnly::ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader) {
|
||||
RefPtr<DOMPointReadOnly> retval = new DOMPointReadOnly(aGlobal);
|
||||
if (!retval->ReadStructuredClone(aReader)) {
|
||||
return nullptr;
|
||||
}
|
||||
return retval.forget();
|
||||
;
|
||||
}
|
||||
|
||||
bool DOMPointReadOnly::ReadStructuredClone(JSStructuredCloneReader* aReader) {
|
||||
uint32_t high;
|
||||
uint32_t low;
|
||||
|
||||
#define ReadDouble(d) \
|
||||
if (!JS_ReadUint32Pair(aReader, &high, &low)) { \
|
||||
return false; \
|
||||
} \
|
||||
(*(d) = BitwiseCast<double>(static_cast<uint64_t>(high) << 32 | low))
|
||||
|
||||
ReadDouble(&mX);
|
||||
ReadDouble(&mY);
|
||||
ReadDouble(&mZ);
|
||||
ReadDouble(&mW);
|
||||
|
||||
return true;
|
||||
#undef ReadDouble
|
||||
}
|
||||
|
||||
already_AddRefed<DOMPoint> DOMPoint::FromPoint(const GlobalObject& aGlobal,
|
||||
const DOMPointInit& aParams) {
|
||||
RefPtr<DOMPoint> obj = new DOMPoint(aGlobal.GetAsSupports(), aParams.mX,
|
||||
@ -44,8 +103,7 @@ already_AddRefed<DOMPoint> DOMPoint::FromPoint(const GlobalObject& aGlobal,
|
||||
|
||||
already_AddRefed<DOMPoint> DOMPoint::Constructor(const GlobalObject& aGlobal,
|
||||
double aX, double aY,
|
||||
double aZ, double aW,
|
||||
ErrorResult& aRV) {
|
||||
double aZ, double aW) {
|
||||
RefPtr<DOMPoint> obj = new DOMPoint(aGlobal.GetAsSupports(), aX, aY, aZ, aW);
|
||||
return obj.forget();
|
||||
}
|
||||
@ -54,3 +112,15 @@ JSObject* DOMPoint::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return DOMPoint_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DOMPoint> DOMPoint::ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader) {
|
||||
RefPtr<DOMPoint> retval = new DOMPoint(aGlobal);
|
||||
if (!retval->ReadStructuredClone(aReader)) {
|
||||
return nullptr;
|
||||
}
|
||||
return retval.forget();
|
||||
;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef MOZILLA_DOMPOINT_H_
|
||||
#define MOZILLA_DOMPOINT_H_
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
@ -13,23 +14,25 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
struct DOMPointInit;
|
||||
struct DOMMatrixInit;
|
||||
|
||||
class DOMPointReadOnly : public nsWrapperCache {
|
||||
public:
|
||||
DOMPointReadOnly(nsISupports* aParent, double aX, double aY, double aZ,
|
||||
double aW)
|
||||
explicit DOMPointReadOnly(nsISupports* aParent, double aX = 0.0,
|
||||
double aY = 0.0, double aZ = 0.0, double aW = 1.0)
|
||||
: mParent(aParent), mX(aX), mY(aY), mZ(aZ), mW(aW) {}
|
||||
|
||||
static already_AddRefed<DOMPointReadOnly> FromPoint(
|
||||
const GlobalObject& aGlobal, const DOMPointInit& aParams);
|
||||
static already_AddRefed<DOMPointReadOnly> Constructor(
|
||||
const GlobalObject& aGlobal, double aX, double aY, double aZ, double aW,
|
||||
ErrorResult& aRV);
|
||||
const GlobalObject& aGlobal, double aX, double aY, double aZ, double aW);
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMPointReadOnly)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMPointReadOnly)
|
||||
@ -39,13 +42,27 @@ class DOMPointReadOnly : public nsWrapperCache {
|
||||
double Z() const { return mZ; }
|
||||
double W() const { return mW; }
|
||||
|
||||
already_AddRefed<DOMPoint> MatrixTransform(const DOMMatrixInit& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
bool WriteStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter) const;
|
||||
|
||||
static already_AddRefed<DOMPointReadOnly> ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader);
|
||||
|
||||
protected:
|
||||
virtual ~DOMPointReadOnly() {}
|
||||
|
||||
// Shared implementation of ReadStructuredClone for DOMPoint and
|
||||
// DOMPointReadOnly.
|
||||
bool ReadStructuredClone(JSStructuredCloneReader* aReader);
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
double mX, mY, mZ, mW;
|
||||
};
|
||||
@ -60,11 +77,16 @@ class DOMPoint final : public DOMPointReadOnly {
|
||||
const DOMPointInit& aParams);
|
||||
static already_AddRefed<DOMPoint> Constructor(const GlobalObject& aGlobal,
|
||||
double aX, double aY, double aZ,
|
||||
double aW, ErrorResult& aRV);
|
||||
double aW);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<DOMPoint> ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader);
|
||||
using DOMPointReadOnly::ReadStructuredClone;
|
||||
|
||||
void SetX(double aX) { mX = aX; }
|
||||
void SetY(double aY) { mY = aY; }
|
||||
void SetZ(double aZ) { mZ = aZ; }
|
||||
|
@ -1,47 +0,0 @@
|
||||
/* 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 "DOMPrefs.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
void DOMPrefs::Initialize() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Let's cache all the values on the main-thread.
|
||||
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
DOMPrefs::DumpEnabled();
|
||||
#endif
|
||||
|
||||
#define DOM_WEBIDL_PREF(name)
|
||||
|
||||
#include "DOMPrefsInternal.h"
|
||||
|
||||
#undef DOM_WEBIDL_PREF
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DOMPrefs::DumpEnabled() {
|
||||
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
return StaticPrefs::browser_dom_window_dump_enabled();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define DOM_WEBIDL_PREF(name) \
|
||||
/* static */ bool DOMPrefs::name(JSContext* aCx, JSObject* aObj) { \
|
||||
return StaticPrefs::name(); \
|
||||
}
|
||||
|
||||
#include "DOMPrefsInternal.h"
|
||||
|
||||
#undef DOM_WEBIDL_PREF
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,29 +0,0 @@
|
||||
/* 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_DOMPrefs_h
|
||||
#define mozilla_dom_DOMPrefs_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMPrefs final {
|
||||
public:
|
||||
// This must be called on the main-thread.
|
||||
static void Initialize();
|
||||
|
||||
// Returns true if the browser.dom.window.dump.enabled pref is set.
|
||||
static bool DumpEnabled();
|
||||
|
||||
#define DOM_WEBIDL_PREF(name) static bool name(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
#include "DOMPrefsInternal.h"
|
||||
|
||||
#undef DOM_WEBIDL_PREF
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_DOMPrefs_h
|
@ -1,31 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
// This is the list of the preferences that are exposed to workers and
|
||||
// main-thread in DOM.
|
||||
// The format is as follows:
|
||||
//
|
||||
// DOM_WEBIDL_PREF(foo_bar)
|
||||
//
|
||||
// * This defines DOMPrefs::foo_bar(JSContext* aCx, JSObject* aObj) which
|
||||
// returns the value of StaticPrefs::foo_bar().
|
||||
// This is allows the use of DOMPrefs in WebIDL files.
|
||||
|
||||
DOM_WEBIDL_PREF(dom_caches_enabled)
|
||||
DOM_WEBIDL_PREF(dom_webnotifications_serviceworker_enabled)
|
||||
DOM_WEBIDL_PREF(dom_webnotifications_requireinteraction_enabled)
|
||||
DOM_WEBIDL_PREF(dom_serviceWorkers_enabled)
|
||||
DOM_WEBIDL_PREF(dom_storageManager_enabled)
|
||||
DOM_WEBIDL_PREF(dom_testing_structuredclonetester_enabled)
|
||||
DOM_WEBIDL_PREF(dom_promise_rejection_events_enabled)
|
||||
DOM_WEBIDL_PREF(dom_push_enabled)
|
||||
DOM_WEBIDL_PREF(gfx_offscreencanvas_enabled)
|
||||
DOM_WEBIDL_PREF(dom_webkitBlink_dirPicker_enabled)
|
||||
DOM_WEBIDL_PREF(dom_netinfo_enabled)
|
||||
DOM_WEBIDL_PREF(dom_fetchObserver_enabled)
|
||||
DOM_WEBIDL_PREF(dom_enable_performance_observer)
|
||||
DOM_WEBIDL_PREF(dom_reporting_enabled)
|
||||
DOM_WEBIDL_PREF(dom_reporting_testing_enabled)
|
||||
DOM_WEBIDL_PREF(dom_reporting_featurePolicy_enabled)
|
||||
DOM_WEBIDL_PREF(javascript_options_streams)
|
@ -7,14 +7,15 @@
|
||||
#include "mozilla/dom/DOMQuadBinding.h"
|
||||
#include "mozilla/dom/DOMPoint.h"
|
||||
#include "mozilla/dom/DOMRect.h"
|
||||
#include "mozilla/dom/DOMRectBinding.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMQuad, mParent, mBounds, mPoints[0],
|
||||
mPoints[1], mPoints[2], mPoints[3])
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMQuad, mParent, mPoints[0], mPoints[1],
|
||||
mPoints[2], mPoints[3])
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMQuad, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMQuad, Release)
|
||||
@ -34,12 +35,35 @@ JSObject* DOMQuad::WrapObject(JSContext* aCx,
|
||||
return DOMQuad_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMQuad> DOMQuad::FromRect(const GlobalObject& aGlobal,
|
||||
const DOMRectInit& aInit) {
|
||||
nsISupports* parent = aGlobal.GetAsSupports();
|
||||
RefPtr<DOMQuad> obj = new DOMQuad(parent);
|
||||
obj->mPoints[0] = new DOMPoint(parent, aInit.mX, aInit.mY, 0, 1);
|
||||
obj->mPoints[1] =
|
||||
new DOMPoint(parent, aInit.mX + aInit.mWidth, aInit.mY, 0, 1);
|
||||
obj->mPoints[2] = new DOMPoint(parent, aInit.mX + aInit.mWidth,
|
||||
aInit.mY + aInit.mHeight, 0, 1);
|
||||
obj->mPoints[3] =
|
||||
new DOMPoint(parent, aInit.mX, aInit.mY + aInit.mHeight, 0, 1);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMQuad> DOMQuad::FromQuad(const GlobalObject& aGlobal,
|
||||
const DOMQuadInit& aInit) {
|
||||
RefPtr<DOMQuad> obj = new DOMQuad(aGlobal.GetAsSupports());
|
||||
obj->mPoints[0] = DOMPoint::FromPoint(aGlobal, aInit.mP1);
|
||||
obj->mPoints[1] = DOMPoint::FromPoint(aGlobal, aInit.mP2);
|
||||
obj->mPoints[2] = DOMPoint::FromPoint(aGlobal, aInit.mP3);
|
||||
obj->mPoints[3] = DOMPoint::FromPoint(aGlobal, aInit.mP4);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMQuad> DOMQuad::Constructor(const GlobalObject& aGlobal,
|
||||
const DOMPointInit& aP1,
|
||||
const DOMPointInit& aP2,
|
||||
const DOMPointInit& aP3,
|
||||
const DOMPointInit& aP4,
|
||||
ErrorResult& aRV) {
|
||||
const DOMPointInit& aP4) {
|
||||
RefPtr<DOMQuad> obj = new DOMQuad(aGlobal.GetAsSupports());
|
||||
obj->mPoints[0] = DOMPoint::FromPoint(aGlobal, aP1);
|
||||
obj->mPoints[1] = DOMPoint::FromPoint(aGlobal, aP2);
|
||||
@ -49,8 +73,7 @@ already_AddRefed<DOMQuad> DOMQuad::Constructor(const GlobalObject& aGlobal,
|
||||
}
|
||||
|
||||
already_AddRefed<DOMQuad> DOMQuad::Constructor(const GlobalObject& aGlobal,
|
||||
const DOMRectReadOnly& aRect,
|
||||
ErrorResult& aRV) {
|
||||
const DOMRectReadOnly& aRect) {
|
||||
CSSPoint points[4];
|
||||
Float x = aRect.X(), y = aRect.Y(), w = aRect.Width(), h = aRect.Height();
|
||||
points[0] = CSSPoint(x, y);
|
||||
@ -85,13 +108,6 @@ void DOMQuad::GetVerticalMinMax(double* aY1, double* aY2) const {
|
||||
*aY2 = y2;
|
||||
}
|
||||
|
||||
DOMRectReadOnly* DOMQuad::Bounds() {
|
||||
if (!mBounds) {
|
||||
mBounds = GetBounds();
|
||||
}
|
||||
return mBounds;
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRectReadOnly> DOMQuad::GetBounds() const {
|
||||
double x1, x2;
|
||||
double y1, y2;
|
||||
@ -104,9 +120,27 @@ already_AddRefed<DOMRectReadOnly> DOMQuad::GetBounds() const {
|
||||
return rval.forget();
|
||||
}
|
||||
|
||||
void DOMQuad::ToJSON(DOMQuadJSON& aInit) {
|
||||
aInit.mP1.Construct(RefPtr<DOMPoint>(P1()).forget());
|
||||
aInit.mP2.Construct(RefPtr<DOMPoint>(P2()).forget());
|
||||
aInit.mP3.Construct(RefPtr<DOMPoint>(P3()).forget());
|
||||
aInit.mP4.Construct(RefPtr<DOMPoint>(P4()).forget());
|
||||
// https://drafts.fxtf.org/geometry/#structured-serialization
|
||||
bool DOMQuad::WriteStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter) const {
|
||||
for (const auto& point : mPoints) {
|
||||
if (!point->WriteStructuredClone(aCx, aWriter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DOMQuad> DOMQuad::ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader) {
|
||||
RefPtr<DOMQuad> quad = new DOMQuad(aGlobal);
|
||||
for (auto& point : quad->mPoints) {
|
||||
point = DOMPoint::ReadStructuredClone(aCx, aGlobal, aReader);
|
||||
if (!point) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return quad.forget();
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef MOZILLA_DOMQUAD_H_
|
||||
#define MOZILLA_DOMQUAD_H_
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
@ -14,13 +15,16 @@
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "Units.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMRectReadOnly;
|
||||
class DOMPoint;
|
||||
struct DOMQuadJSON;
|
||||
struct DOMPointInit;
|
||||
struct DOMQuadInit;
|
||||
struct DOMRectInit;
|
||||
|
||||
class DOMQuad final : public nsWrapperCache {
|
||||
~DOMQuad();
|
||||
@ -36,17 +40,20 @@ class DOMQuad final : public nsWrapperCache {
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<DOMQuad> FromRect(const GlobalObject& aGlobal,
|
||||
const DOMRectInit& aInit);
|
||||
|
||||
static already_AddRefed<DOMQuad> FromQuad(const GlobalObject& aGlobal,
|
||||
const DOMQuadInit& aInit);
|
||||
|
||||
static already_AddRefed<DOMQuad> Constructor(const GlobalObject& aGlobal,
|
||||
const DOMPointInit& aP1,
|
||||
const DOMPointInit& aP2,
|
||||
const DOMPointInit& aP3,
|
||||
const DOMPointInit& aP4,
|
||||
ErrorResult& aRV);
|
||||
const DOMPointInit& aP4);
|
||||
static already_AddRefed<DOMQuad> Constructor(const GlobalObject& aGlobal,
|
||||
const DOMRectReadOnly& aRect,
|
||||
ErrorResult& aRV);
|
||||
const DOMRectReadOnly& aRect);
|
||||
|
||||
DOMRectReadOnly* Bounds();
|
||||
already_AddRefed<DOMRectReadOnly> GetBounds() const;
|
||||
DOMPoint* P1() const { return mPoints[0]; }
|
||||
DOMPoint* P2() const { return mPoints[1]; }
|
||||
@ -55,7 +62,12 @@ class DOMQuad final : public nsWrapperCache {
|
||||
|
||||
DOMPoint* Point(uint32_t aIndex) const { return mPoints[aIndex]; }
|
||||
|
||||
void ToJSON(DOMQuadJSON& aInit);
|
||||
bool WriteStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter) const;
|
||||
|
||||
static already_AddRefed<DOMQuad> ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader);
|
||||
|
||||
protected:
|
||||
void GetHorizontalMinMax(double* aX1, double* aX2) const;
|
||||
@ -63,7 +75,6 @@ class DOMQuad final : public nsWrapperCache {
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
RefPtr<DOMPoint> mPoints[4];
|
||||
RefPtr<DOMRectReadOnly> mBounds;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -25,14 +25,65 @@ JSObject* DOMRectReadOnly::WrapObject(JSContext* aCx,
|
||||
return DOMRectReadOnly_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRectReadOnly> DOMRectReadOnly::FromRect(
|
||||
const GlobalObject& aGlobal, const DOMRectInit& aInit) {
|
||||
RefPtr<DOMRectReadOnly> obj = new DOMRectReadOnly(
|
||||
aGlobal.GetAsSupports(), aInit.mX, aInit.mY, aInit.mWidth, aInit.mHeight);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRectReadOnly> DOMRectReadOnly::Constructor(
|
||||
const GlobalObject& aGlobal, double aX, double aY, double aWidth,
|
||||
double aHeight, ErrorResult& aRv) {
|
||||
double aHeight) {
|
||||
RefPtr<DOMRectReadOnly> obj =
|
||||
new DOMRectReadOnly(aGlobal.GetAsSupports(), aX, aY, aWidth, aHeight);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
// https://drafts.fxtf.org/geometry/#structured-serialization
|
||||
bool DOMRectReadOnly::WriteStructuredClone(
|
||||
JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
|
||||
#define WriteDouble(d) \
|
||||
JS_WriteUint32Pair(aWriter, (BitwiseCast<uint64_t>(d) >> 32) & 0xffffffff, \
|
||||
BitwiseCast<uint64_t>(d) & 0xffffffff)
|
||||
|
||||
return WriteDouble(mX) && WriteDouble(mY) && WriteDouble(mWidth) &&
|
||||
WriteDouble(mHeight);
|
||||
|
||||
#undef WriteDouble
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DOMRectReadOnly> DOMRectReadOnly::ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader) {
|
||||
RefPtr<DOMRectReadOnly> retval = new DOMRectReadOnly(aGlobal);
|
||||
if (!retval->ReadStructuredClone(aReader)) {
|
||||
return nullptr;
|
||||
}
|
||||
return retval.forget();
|
||||
}
|
||||
|
||||
bool DOMRectReadOnly::ReadStructuredClone(JSStructuredCloneReader* aReader) {
|
||||
uint32_t high;
|
||||
uint32_t low;
|
||||
|
||||
#define ReadDouble(d) \
|
||||
if (!JS_ReadUint32Pair(aReader, &high, &low)) { \
|
||||
return false; \
|
||||
} \
|
||||
(*(d) = BitwiseCast<double>(static_cast<uint64_t>(high) << 32 | low))
|
||||
|
||||
ReadDouble(&mX);
|
||||
ReadDouble(&mY);
|
||||
ReadDouble(&mWidth);
|
||||
ReadDouble(&mHeight);
|
||||
|
||||
return true;
|
||||
|
||||
#undef ReadDouble
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
JSObject* DOMRect::WrapObject(JSContext* aCx,
|
||||
@ -41,15 +92,32 @@ JSObject* DOMRect::WrapObject(JSContext* aCx,
|
||||
return DOMRect_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRect> DOMRect::FromRect(const GlobalObject& aGlobal,
|
||||
const DOMRectInit& aInit) {
|
||||
RefPtr<DOMRect> obj = new DOMRect(aGlobal.GetAsSupports(), aInit.mX, aInit.mY,
|
||||
aInit.mWidth, aInit.mHeight);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRect> DOMRect::Constructor(const GlobalObject& aGlobal,
|
||||
double aX, double aY,
|
||||
double aWidth, double aHeight,
|
||||
ErrorResult& aRv) {
|
||||
double aWidth, double aHeight) {
|
||||
RefPtr<DOMRect> obj =
|
||||
new DOMRect(aGlobal.GetAsSupports(), aX, aY, aWidth, aHeight);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DOMRect> DOMRect::ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader) {
|
||||
RefPtr<DOMRect> retval = new DOMRect(aGlobal);
|
||||
if (!retval->ReadStructuredClone(aReader)) {
|
||||
return nullptr;
|
||||
}
|
||||
return retval.forget();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMRectList, mParent, mArray)
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef MOZILLA_DOMRECT_H_
|
||||
#define MOZILLA_DOMRECT_H_
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsWrapperCache.h"
|
||||
@ -15,10 +16,13 @@
|
||||
#include <algorithm>
|
||||
|
||||
struct nsRect;
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct DOMRectInit;
|
||||
|
||||
class DOMRectReadOnly : public nsISupports, public nsWrapperCache {
|
||||
protected:
|
||||
virtual ~DOMRectReadOnly() {}
|
||||
@ -39,9 +43,12 @@ class DOMRectReadOnly : public nsISupports, public nsWrapperCache {
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<DOMRectReadOnly> FromRect(const GlobalObject& aGlobal,
|
||||
const DOMRectInit& aInit);
|
||||
|
||||
static already_AddRefed<DOMRectReadOnly> Constructor(
|
||||
const GlobalObject& aGlobal, double aX, double aY, double aWidth,
|
||||
double aHeight, ErrorResult& aRv);
|
||||
double aHeight);
|
||||
|
||||
double X() const { return mX; }
|
||||
double Y() const { return mY; }
|
||||
@ -65,7 +72,18 @@ class DOMRectReadOnly : public nsISupports, public nsWrapperCache {
|
||||
return std::max(y, y + h);
|
||||
}
|
||||
|
||||
bool WriteStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter) const;
|
||||
|
||||
static already_AddRefed<DOMRectReadOnly> ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader);
|
||||
|
||||
protected:
|
||||
// Shared implementation of ReadStructuredClone for DOMRect and
|
||||
// DOMRectReadOnly.
|
||||
bool ReadStructuredClone(JSStructuredCloneReader* aReader);
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
double mX, mY, mWidth, mHeight;
|
||||
};
|
||||
@ -78,14 +96,21 @@ class DOMRect final : public DOMRectReadOnly {
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING_INHERITED(DOMRect, DOMRectReadOnly)
|
||||
|
||||
static already_AddRefed<DOMRect> FromRect(const GlobalObject& aGlobal,
|
||||
const DOMRectInit& aInit);
|
||||
|
||||
static already_AddRefed<DOMRect> Constructor(const GlobalObject& aGlobal,
|
||||
double aX, double aY,
|
||||
double aWidth, double aHeight,
|
||||
ErrorResult& aRv);
|
||||
double aWidth, double aHeight);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<DOMRect> ReadStructuredClone(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
JSStructuredCloneReader* aReader);
|
||||
using DOMRectReadOnly::ReadStructuredClone;
|
||||
|
||||
void SetRect(float aX, float aY, float aWidth, float aHeight) {
|
||||
mX = aX;
|
||||
mY = aY;
|
||||
|
@ -222,11 +222,12 @@
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::dom::Element;
|
||||
using mozilla::dom::HTMLSlotElement;
|
||||
using mozilla::dom::ShadowRoot;
|
||||
|
||||
static nsIContent* GetParentOrHostOrSlot(
|
||||
nsIContent* aContent, bool* aCrossedShadowBoundary = nullptr) {
|
||||
HTMLSlotElement* slot = aContent->GetAssignedSlot();
|
||||
mozilla::dom::HTMLSlotElement* slot = aContent->GetAssignedSlot();
|
||||
if (slot) {
|
||||
if (aCrossedShadowBoundary) {
|
||||
*aCrossedShadowBoundary = true;
|
||||
@ -376,14 +377,14 @@ static Directionality GetDirectionFromText(const char* aText,
|
||||
return eDir_NotSet;
|
||||
}
|
||||
|
||||
static Directionality GetDirectionFromText(const nsTextFragment* aFrag,
|
||||
static Directionality GetDirectionFromText(const mozilla::dom::Text* aTextNode,
|
||||
uint32_t* aFirstStrong = nullptr) {
|
||||
if (aFrag->Is2b()) {
|
||||
return GetDirectionFromText(aFrag->Get2b(), aFrag->GetLength(),
|
||||
aFirstStrong);
|
||||
const nsTextFragment* frag = &aTextNode->TextFragment();
|
||||
if (frag->Is2b()) {
|
||||
return GetDirectionFromText(frag->Get2b(), frag->GetLength(), aFirstStrong);
|
||||
}
|
||||
|
||||
return GetDirectionFromText(aFrag->Get1b(), aFrag->GetLength(), aFirstStrong);
|
||||
return GetDirectionFromText(frag->Get1b(), frag->GetLength(), aFirstStrong);
|
||||
}
|
||||
|
||||
static nsTextNode* WalkDescendantsAndGetDirectionFromText(
|
||||
@ -397,18 +398,19 @@ static nsTextNode* WalkDescendantsAndGetDirectionFromText(
|
||||
continue;
|
||||
}
|
||||
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
mozilla::dom::HTMLSlotElement* slot =
|
||||
mozilla::dom::HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
nsIContent* assignedNode = assignedNodes[i]->AsContent();
|
||||
if (assignedNode->NodeType() == nsINode::TEXT_NODE) {
|
||||
auto text = static_cast<nsTextNode*>(assignedNode);
|
||||
if (assignedNode != aSkip) {
|
||||
Directionality textNodeDir =
|
||||
GetDirectionFromText(assignedNode->GetText());
|
||||
Directionality textNodeDir = GetDirectionFromText(text);
|
||||
if (textNodeDir != eDir_NotSet) {
|
||||
*aDirectionality = textNodeDir;
|
||||
return static_cast<nsTextNode*>(assignedNode);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
} else if (assignedNode->IsElement() &&
|
||||
@ -424,10 +426,11 @@ static nsTextNode* WalkDescendantsAndGetDirectionFromText(
|
||||
}
|
||||
|
||||
if (child->NodeType() == nsINode::TEXT_NODE && child != aSkip) {
|
||||
Directionality textNodeDir = GetDirectionFromText(child->GetText());
|
||||
auto text = static_cast<nsTextNode*>(child);
|
||||
Directionality textNodeDir = GetDirectionFromText(text);
|
||||
if (textNodeDir != eDir_NotSet) {
|
||||
*aDirectionality = textNodeDir;
|
||||
return static_cast<nsTextNode*>(child);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
child = child->GetNextNode(aRoot);
|
||||
@ -724,7 +727,8 @@ static void SetDirectionalityOnDescendantsInternal(nsINode* aNode,
|
||||
SetDirectionalityOnDescendantsInternal(shadow, aDir, aNotify);
|
||||
}
|
||||
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
mozilla::dom::HTMLSlotElement* slot =
|
||||
mozilla::dom::HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
@ -816,7 +820,7 @@ void WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify) {
|
||||
}
|
||||
}
|
||||
|
||||
void SlotStateChanged(HTMLSlotElement* aSlot) {
|
||||
void SlotStateChanged(mozilla::dom::HTMLSlotElement* aSlot) {
|
||||
if (!aSlot) {
|
||||
return;
|
||||
}
|
||||
@ -889,7 +893,8 @@ static void SetAncestorHasDirAutoOnDescendants(nsINode* aRoot) {
|
||||
if (!child->GetAssignedSlot()) {
|
||||
MaybeSetAncestorHasDirAutoOnShadowDOM(child);
|
||||
child->SetAncestorHasDirAuto();
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
mozilla::dom::HTMLSlotElement* slot =
|
||||
mozilla::dom::HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
@ -942,7 +947,8 @@ void WalkDescendantsClearAncestorDirAuto(nsIContent* aContent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
mozilla::dom::HTMLSlotElement* slot =
|
||||
mozilla::dom::HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
@ -1050,7 +1056,7 @@ void SetAncestorDirectionIfAuto(nsTextNode* aTextNode, Directionality aDir,
|
||||
}
|
||||
}
|
||||
|
||||
bool TextNodeWillChangeDirection(nsIContent* aTextNode, Directionality* aOldDir,
|
||||
bool TextNodeWillChangeDirection(nsTextNode* aTextNode, Directionality* aOldDir,
|
||||
uint32_t aOffset) {
|
||||
if (!NodeAffectsDirAutoAncestor(aTextNode)) {
|
||||
nsTextNodeDirectionalityMap::EnsureMapIsClearFor(aTextNode);
|
||||
@ -1058,13 +1064,13 @@ bool TextNodeWillChangeDirection(nsIContent* aTextNode, Directionality* aOldDir,
|
||||
}
|
||||
|
||||
uint32_t firstStrong;
|
||||
*aOldDir = GetDirectionFromText(aTextNode->GetText(), &firstStrong);
|
||||
*aOldDir = GetDirectionFromText(aTextNode, &firstStrong);
|
||||
return (aOffset <= firstStrong);
|
||||
}
|
||||
|
||||
void TextNodeChangedDirection(nsTextNode* aTextNode, Directionality aOldDir,
|
||||
bool aNotify) {
|
||||
Directionality newDir = GetDirectionFromText(aTextNode->GetText());
|
||||
Directionality newDir = GetDirectionFromText(aTextNode);
|
||||
if (newDir == eDir_NotSet) {
|
||||
if (aOldDir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
|
||||
// This node used to have a strong directional character but no
|
||||
@ -1100,7 +1106,7 @@ void SetDirectionFromNewTextNode(nsTextNode* aTextNode) {
|
||||
aTextNode->SetAncestorHasDirAuto();
|
||||
}
|
||||
|
||||
Directionality dir = GetDirectionFromText(aTextNode->GetText());
|
||||
Directionality dir = GetDirectionFromText(aTextNode);
|
||||
if (dir != eDir_NotSet) {
|
||||
SetAncestorDirectionIfAuto(aTextNode, dir);
|
||||
}
|
||||
@ -1112,7 +1118,7 @@ void ResetDirectionSetByTextNode(nsTextNode* aTextNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
Directionality dir = GetDirectionFromText(aTextNode->GetText());
|
||||
Directionality dir = GetDirectionFromText(aTextNode);
|
||||
if (dir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
|
||||
nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode, aTextNode);
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ void WalkDescendantsClearAncestorDirAuto(nsIContent* aContent);
|
||||
*
|
||||
* @return whether the text node affects the directionality of any element
|
||||
*/
|
||||
bool TextNodeWillChangeDirection(nsIContent* aTextNode, Directionality* aOldDir,
|
||||
bool TextNodeWillChangeDirection(nsTextNode* aTextNode, Directionality* aOldDir,
|
||||
uint32_t aOffset);
|
||||
|
||||
/**
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "nsINamed.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -7,9 +7,9 @@
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/PerformanceUtils.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/ThrottledEventQueue.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#if defined(XP_WIN)
|
||||
@ -63,6 +63,10 @@ DocGroup::~DocGroup() {
|
||||
}
|
||||
|
||||
mTabGroup->mDocGroups.RemoveEntry(mKey);
|
||||
|
||||
if (mIframePostMessageQueue) {
|
||||
FlushIframePostMessageQueue();
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
|
||||
@ -99,7 +103,7 @@ RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
|
||||
if (!win) {
|
||||
continue;
|
||||
}
|
||||
top = win->GetTop();
|
||||
top = win->GetInProcessTop();
|
||||
if (!top) {
|
||||
continue;
|
||||
}
|
||||
@ -186,6 +190,57 @@ void DocGroup::SignalSlotChange(HTMLSlotElement& aSlot) {
|
||||
sPendingDocGroups->AppendElement(this);
|
||||
}
|
||||
|
||||
bool DocGroup::TryToLoadIframesInBackground() {
|
||||
return StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
|
||||
StaticPrefs::dom_cross_origin_iframes_loaded_in_background();
|
||||
}
|
||||
|
||||
nsresult DocGroup::QueueIframePostMessages(
|
||||
already_AddRefed<nsIRunnable>&& aRunnable, uint64_t aWindowId) {
|
||||
if (DocGroup::TryToLoadIframesInBackground()) {
|
||||
if (!mIframePostMessageQueue) {
|
||||
nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
|
||||
mIframePostMessageQueue = ThrottledEventQueue::Create(
|
||||
target, "Background Loading Iframe PostMessage Queue",
|
||||
nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
|
||||
nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
}
|
||||
|
||||
// Ensure the queue is disabled. Unlike the postMessageEvent queue in
|
||||
// TabGroup, this postMessage queue should always be paused, because if
|
||||
// we leave it open, the postMessage may get dispatched to an unloaded
|
||||
// iframe
|
||||
MOZ_ASSERT(mIframePostMessageQueue);
|
||||
MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
|
||||
|
||||
mIframesUsedPostMessageQueue.PutEntry(aWindowId);
|
||||
|
||||
mIframePostMessageQueue->Dispatch(std::move(aRunnable), NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void DocGroup::TryFlushIframePostMessages(uint64_t aWindowId) {
|
||||
if (DocGroup::TryToLoadIframesInBackground()) {
|
||||
mIframesUsedPostMessageQueue.RemoveEntry(aWindowId);
|
||||
if (mIframePostMessageQueue && mIframesUsedPostMessageQueue.IsEmpty()) {
|
||||
MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
|
||||
nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
FlushIframePostMessageQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DocGroup::FlushIframePostMessageQueue() {
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
while ((event = mIframePostMessageQueue->GetEvent())) {
|
||||
Dispatch(TaskCategory::Other, event.forget());
|
||||
}
|
||||
}
|
||||
|
||||
void DocGroup::MoveSignalSlotListTo(nsTArray<RefPtr<HTMLSlotElement>>& aDest) {
|
||||
aDest.SetCapacity(aDest.Length() + mSignalSlotList.Length());
|
||||
for (RefPtr<HTMLSlotElement>& slot : mSignalSlotList) {
|
||||
|
@ -103,16 +103,27 @@ class DocGroup final {
|
||||
// Returns true if any of its documents are active but not in the bfcache.
|
||||
bool IsActive() const;
|
||||
|
||||
nsresult QueueIframePostMessages(already_AddRefed<nsIRunnable>&& aRunnable,
|
||||
uint64_t aWindowId);
|
||||
|
||||
void TryFlushIframePostMessages(uint64_t aWindowId);
|
||||
|
||||
static bool TryToLoadIframesInBackground();
|
||||
|
||||
private:
|
||||
DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
|
||||
~DocGroup();
|
||||
|
||||
void FlushIframePostMessageQueue();
|
||||
nsCString mKey;
|
||||
RefPtr<TabGroup> mTabGroup;
|
||||
nsTArray<Document*> mDocuments;
|
||||
RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack;
|
||||
nsTArray<RefPtr<HTMLSlotElement>> mSignalSlotList;
|
||||
RefPtr<mozilla::PerformanceCounter> mPerformanceCounter;
|
||||
|
||||
RefPtr<mozilla::ThrottledEventQueue> mIframePostMessageQueue;
|
||||
nsTHashtable<nsUint64HashKey> mIframesUsedPostMessageQueue;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -57,13 +57,12 @@ class DocumentFragment : public FragmentOrElement {
|
||||
|
||||
virtual bool IsNodeOfType(uint32_t aFlags) const override;
|
||||
|
||||
nsresult BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent) override {
|
||||
nsresult BindToTree(BindContext&, nsINode& aParent) override {
|
||||
NS_ASSERTION(false, "Trying to bind a fragment to a tree");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
virtual void UnbindFromTree(bool aDeep, bool aNullParent) override {
|
||||
virtual void UnbindFromTree(bool aNullParent) override {
|
||||
NS_ASSERTION(false, "Trying to unbind a fragment from a tree");
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "mozilla/dom/DocumentTypeBinding.h"
|
||||
|
@ -12,11 +12,15 @@
|
||||
|
||||
#include "AnimationCommon.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_layout.h"
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/Attr.h"
|
||||
#include "mozilla/dom/BindContext.h"
|
||||
#include "mozilla/dom/Flex.h"
|
||||
#include "mozilla/dom/Grid.h"
|
||||
#include "mozilla/dom/Link.h"
|
||||
#include "mozilla/dom/MutationObservers.h"
|
||||
#include "mozilla/dom/ScriptLoader.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
@ -28,7 +32,6 @@
|
||||
#include "nsFlexContainerFrame.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsPresContext.h"
|
||||
@ -36,7 +39,6 @@
|
||||
#include "nsString.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsDOMCID.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsDOMCSSAttrDeclaration.h"
|
||||
#include "nsNameSpaceManager.h"
|
||||
#include "nsContentList.h"
|
||||
@ -45,7 +47,6 @@
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
#include "nsError.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "mozilla/dom/AnimatableBinding.h"
|
||||
#include "mozilla/dom/FeaturePolicyUtils.h"
|
||||
#include "mozilla/dom/HTMLDivElement.h"
|
||||
@ -68,9 +69,9 @@
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "mozilla/ScrollTypes.h"
|
||||
#include "mozilla/SizeOfState.h"
|
||||
#include "mozilla/TextControlElement.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "mozilla/dom/DirectionalityUtils.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
@ -97,16 +98,11 @@
|
||||
#include "nsGkAtoms.h"
|
||||
#include "ChildIterator.h"
|
||||
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsIControllers.h"
|
||||
#include "nsView.h"
|
||||
#include "nsViewManager.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
@ -129,7 +125,6 @@
|
||||
|
||||
#include "nsStyledElement.h"
|
||||
#include "nsXBLService.h"
|
||||
#include "nsITextControlElement.h"
|
||||
#include "nsITextControlFrame.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/dom/CSSPseudoElement.h"
|
||||
@ -160,7 +155,6 @@
|
||||
#include "nsIAutoCompletePopup.h"
|
||||
|
||||
#include "nsISpeculativeConnect.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsBlockFrame.h"
|
||||
|
||||
#include "DOMMatrix.h"
|
||||
@ -542,19 +536,19 @@ static bool MayNeedToLoadXBLBinding(const Document& aDocument,
|
||||
return aElement.IsAnyOfHTMLElements(nsGkAtoms::object, nsGkAtoms::embed);
|
||||
}
|
||||
|
||||
bool Element::GetBindingURL(Document* aDocument, css::URLValue** aResult) {
|
||||
StyleUrlOrNone Element::GetBindingURL(Document* aDocument) {
|
||||
if (!MayNeedToLoadXBLBinding(*aDocument, *this)) {
|
||||
*aResult = nullptr;
|
||||
return true;
|
||||
return StyleUrlOrNone::None();
|
||||
}
|
||||
|
||||
// Get the computed -moz-binding directly from the ComputedStyle
|
||||
RefPtr<ComputedStyle> sc =
|
||||
RefPtr<ComputedStyle> style =
|
||||
nsComputedDOMStyle::GetComputedStyleNoFlush(this, nullptr);
|
||||
NS_ENSURE_TRUE(sc, false);
|
||||
if (!style) {
|
||||
return StyleUrlOrNone::None();
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aResult = sc->StyleDisplay()->mBinding);
|
||||
return true;
|
||||
return style->StyleDisplay()->mBinding;
|
||||
}
|
||||
|
||||
JSObject* Element::WrapObject(JSContext* aCx,
|
||||
@ -594,17 +588,11 @@ JSObject* Element::WrapObject(JSContext* aCx,
|
||||
// since that can destroy the relevant presshell.
|
||||
|
||||
{
|
||||
// Make a scope so that ~nsRefPtr can GC before returning obj.
|
||||
RefPtr<css::URLValue> bindingURL;
|
||||
bool ok = GetBindingURL(doc, getter_AddRefs(bindingURL));
|
||||
if (!ok) {
|
||||
dom::Throw(aCx, NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (bindingURL) {
|
||||
nsCOMPtr<nsIURI> uri = bindingURL->GetURI();
|
||||
nsCOMPtr<nsIPrincipal> principal = bindingURL->ExtraData()->Principal();
|
||||
StyleUrlOrNone result = GetBindingURL(doc);
|
||||
if (result.IsUrl()) {
|
||||
auto& url = result.AsUrl();
|
||||
nsCOMPtr<nsIURI> uri = url.GetURI();
|
||||
nsCOMPtr<nsIPrincipal> principal = url.ExtraData().Principal();
|
||||
|
||||
// We have a binding that must be installed.
|
||||
nsXBLService* xblService = nsXBLService::GetInstance();
|
||||
@ -770,15 +758,13 @@ void Element::ScrollIntoView(const ScrollIntoViewOptions& aOptions) {
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected ScrollLogicalPosition value");
|
||||
}
|
||||
|
||||
ScrollFlags scrollFlags = ScrollFlags::ScrollOverflowHidden;
|
||||
ScrollFlags scrollFlags =
|
||||
ScrollFlags::ScrollOverflowHidden | ScrollFlags::ScrollSnap;
|
||||
if (aOptions.mBehavior == ScrollBehavior::Smooth) {
|
||||
scrollFlags |= ScrollFlags::ScrollSmooth;
|
||||
} else if (aOptions.mBehavior == ScrollBehavior::Auto) {
|
||||
scrollFlags |= ScrollFlags::ScrollSmoothAuto;
|
||||
}
|
||||
if (StaticPrefs::layout_css_scroll_snap_v1_enabled()) {
|
||||
scrollFlags |= ScrollFlags::ScrollSnap;
|
||||
}
|
||||
|
||||
presShell->ScrollContentIntoView(
|
||||
this, ScrollAxis(whereToScrollVertically, WhenToScroll::Always),
|
||||
@ -789,15 +775,9 @@ void Element::Scroll(const CSSIntPoint& aScroll,
|
||||
const ScrollOptions& aOptions) {
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
ScrollMode scrollMode = ScrollMode::Instant;
|
||||
if (aOptions.mBehavior == ScrollBehavior::Smooth) {
|
||||
scrollMode = ScrollMode::SmoothMsd;
|
||||
} else if (aOptions.mBehavior == ScrollBehavior::Auto) {
|
||||
ScrollStyles styles = sf->GetScrollStyles();
|
||||
if (styles.mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
|
||||
scrollMode = ScrollMode::SmoothMsd;
|
||||
}
|
||||
}
|
||||
ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
|
||||
? ScrollMode::SmoothMsd
|
||||
: ScrollMode::Instant;
|
||||
|
||||
sf->ScrollToCSSPixels(aScroll, scrollMode);
|
||||
}
|
||||
@ -852,15 +832,9 @@ void Element::ScrollBy(const ScrollToOptions& aOptions) {
|
||||
scrollDelta.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
|
||||
}
|
||||
|
||||
ScrollMode scrollMode = ScrollMode::Instant;
|
||||
if (aOptions.mBehavior == ScrollBehavior::Smooth) {
|
||||
scrollMode = ScrollMode::SmoothMsd;
|
||||
} else if (aOptions.mBehavior == ScrollBehavior::Auto) {
|
||||
ScrollStyles styles = sf->GetScrollStyles();
|
||||
if (styles.mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
|
||||
scrollMode = ScrollMode::SmoothMsd;
|
||||
}
|
||||
}
|
||||
ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
|
||||
? ScrollMode::SmoothMsd
|
||||
: ScrollMode::Instant;
|
||||
|
||||
sf->ScrollByCSSPixels(scrollDelta, scrollMode, nsGkAtoms::relative);
|
||||
}
|
||||
@ -882,11 +856,9 @@ void Element::SetScrollTop(int32_t aScrollTop) {
|
||||
FlushType flushType = aScrollTop == 0 ? FlushType::Frames : FlushType::Layout;
|
||||
nsIScrollableFrame* sf = GetScrollFrame(nullptr, flushType);
|
||||
if (sf) {
|
||||
ScrollMode scrollMode = ScrollMode::Instant;
|
||||
if (sf->GetScrollStyles().mScrollBehavior ==
|
||||
NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
|
||||
scrollMode = ScrollMode::SmoothMsd;
|
||||
}
|
||||
ScrollMode scrollMode =
|
||||
sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
|
||||
|
||||
sf->ScrollToCSSPixels(
|
||||
CSSIntPoint(sf->GetScrollPositionCSSPixels().x, aScrollTop),
|
||||
scrollMode);
|
||||
@ -904,11 +876,8 @@ void Element::SetScrollLeft(int32_t aScrollLeft) {
|
||||
// range. So we need to flush layout no matter what.
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
ScrollMode scrollMode = ScrollMode::Instant;
|
||||
if (sf->GetScrollStyles().mScrollBehavior ==
|
||||
NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
|
||||
scrollMode = ScrollMode::SmoothMsd;
|
||||
}
|
||||
ScrollMode scrollMode =
|
||||
sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
|
||||
|
||||
sf->ScrollToCSSPixels(
|
||||
CSSIntPoint(aScrollLeft, sf->GetScrollPositionCSSPixels().y),
|
||||
@ -1138,8 +1107,8 @@ bool Element::CanAttachShadowDOM() const {
|
||||
* return false.
|
||||
*/
|
||||
nsAtom* nameAtom = NodeInfo()->NameAtom();
|
||||
if (!(nsContentUtils::IsCustomElementName(nameAtom,
|
||||
NodeInfo()->NamespaceID()) ||
|
||||
uint32_t namespaceID = NodeInfo()->NamespaceID();
|
||||
if (!(nsContentUtils::IsCustomElementName(nameAtom, namespaceID) ||
|
||||
nameAtom == nsGkAtoms::article || nameAtom == nsGkAtoms::aside ||
|
||||
nameAtom == nsGkAtoms::blockquote || nameAtom == nsGkAtoms::body ||
|
||||
nameAtom == nsGkAtoms::div || nameAtom == nsGkAtoms::footer ||
|
||||
@ -1152,6 +1121,30 @@ bool Element::CanAttachShadowDOM() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. If context object’s local name is a valid custom element name, or
|
||||
* context object’s is value is not null, then:
|
||||
* If definition is not null and definition’s disable shadow is true, then
|
||||
* return false.
|
||||
*/
|
||||
// It will always have CustomElementData when the element is a valid custom
|
||||
// element or has is value.
|
||||
CustomElementData* ceData = GetCustomElementData();
|
||||
if (StaticPrefs::dom_webcomponents_elementInternals_enabled() && ceData) {
|
||||
CustomElementDefinition* definition = ceData->GetCustomElementDefinition();
|
||||
// If the definition is null, the element possible hasn't yet upgraded.
|
||||
// Fallback to use LookupCustomElementDefinition to find its definition.
|
||||
if (!definition) {
|
||||
definition = nsContentUtils::LookupCustomElementDefinition(
|
||||
NodeInfo()->GetDocument(), nameAtom, namespaceID,
|
||||
ceData->GetCustomElementType());
|
||||
}
|
||||
|
||||
if (definition && definition->mDisableShadow) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1170,11 +1163,11 @@ already_AddRefed<ShadowRoot> Element::AttachShadow(const ShadowRootInit& aInit,
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. If context object is a shadow host, then throw
|
||||
* an "InvalidStateError" DOMException.
|
||||
* 4. If context object is a shadow host, then throw
|
||||
* an "NotSupportedError" DOMException.
|
||||
*/
|
||||
if (GetShadowRoot() || GetXBLBinding()) {
|
||||
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1578,133 +1571,103 @@ void Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements) {
|
||||
}
|
||||
}
|
||||
|
||||
nsresult Element::BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent) {
|
||||
MOZ_ASSERT(aParent || aDocument, "Must have document if no parent!");
|
||||
MOZ_ASSERT((NODE_FROM(aParent, aDocument)->OwnerDoc() == OwnerDoc()),
|
||||
nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||
MOZ_ASSERT(aParent.IsContent() || aParent.IsDocument(),
|
||||
"Must have content or document parent!");
|
||||
MOZ_ASSERT(aParent.OwnerDoc() == OwnerDoc(),
|
||||
"Must have the same owner document");
|
||||
MOZ_ASSERT(!aParent || aDocument == aParent->GetUncomposedDoc(),
|
||||
"aDocument must be current doc of aParent");
|
||||
MOZ_ASSERT(!IsInComposedDoc(), "Already have a document. Unbind first!");
|
||||
MOZ_ASSERT(OwnerDoc() == &aContext.OwnerDoc(), "These should match too");
|
||||
MOZ_ASSERT(!IsInUncomposedDoc(), "Already have a document. Unbind first!");
|
||||
MOZ_ASSERT(!IsInComposedDoc(), "Already have a document. Unbind first!");
|
||||
// Note that as we recurse into the kids, they'll have a non-null parent. So
|
||||
// only assert if our parent is _changing_ while we have a parent.
|
||||
MOZ_ASSERT(!GetParent() || aParent == GetParent(),
|
||||
MOZ_ASSERT(!GetParentNode() || &aParent == GetParentNode(),
|
||||
"Already have a parent. Unbind first!");
|
||||
MOZ_ASSERT(!GetBindingParent() || aBindingParent == GetBindingParent() ||
|
||||
(!aBindingParent && aParent &&
|
||||
aParent->GetBindingParent() == GetBindingParent()),
|
||||
MOZ_ASSERT(
|
||||
!GetBindingParent() ||
|
||||
aContext.GetBindingParent() == GetBindingParent() ||
|
||||
(!aContext.GetBindingParent() && aParent.IsContent() &&
|
||||
aParent.AsContent()->GetBindingParent() == GetBindingParent()),
|
||||
"Already have a binding parent. Unbind first!");
|
||||
MOZ_ASSERT(aBindingParent != this,
|
||||
MOZ_ASSERT(aContext.GetBindingParent() != this,
|
||||
"Content must not be its own binding parent");
|
||||
MOZ_ASSERT(!IsRootOfNativeAnonymousSubtree() || aBindingParent == aParent,
|
||||
MOZ_ASSERT(!IsRootOfNativeAnonymousSubtree() ||
|
||||
aContext.GetBindingParent() == &aParent,
|
||||
"Native anonymous content must have its parent as its "
|
||||
"own binding parent");
|
||||
MOZ_ASSERT(aBindingParent || !aParent ||
|
||||
aBindingParent == aParent->GetBindingParent(),
|
||||
MOZ_ASSERT(aContext.GetBindingParent() || !aParent.IsContent() ||
|
||||
aContext.GetBindingParent() ==
|
||||
aParent.AsContent()->GetBindingParent(),
|
||||
"We should be passed the right binding parent");
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
// First set the binding parent
|
||||
nsXULElement* xulElem = nsXULElement::FromNode(this);
|
||||
if (xulElem) {
|
||||
xulElem->SetXULBindingParent(aBindingParent);
|
||||
if (nsXULElement* xulElem = nsXULElement::FromNode(this)) {
|
||||
xulElem->SetXULBindingParent(aContext.GetBindingParent());
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (aBindingParent) {
|
||||
nsExtendedDOMSlots* slots = ExtendedDOMSlots();
|
||||
|
||||
slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
|
||||
if (Element* bindingParent = aContext.GetBindingParent()) {
|
||||
ExtendedDOMSlots()->mBindingParent = bindingParent;
|
||||
}
|
||||
}
|
||||
|
||||
const bool hadParent = !!GetParentNode();
|
||||
const bool wasInShadowTree = IsInShadowTree();
|
||||
|
||||
NS_ASSERTION(!aBindingParent || IsRootOfNativeAnonymousSubtree() ||
|
||||
NS_ASSERTION(!aContext.GetBindingParent() ||
|
||||
IsRootOfNativeAnonymousSubtree() ||
|
||||
!HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) ||
|
||||
(aParent && aParent->IsInNativeAnonymousSubtree()),
|
||||
aParent.IsInNativeAnonymousSubtree(),
|
||||
"Trying to re-bind content from native anonymous subtree to "
|
||||
"non-native anonymous parent!");
|
||||
if (aParent) {
|
||||
if (aParent->IsInNativeAnonymousSubtree()) {
|
||||
if (aParent.IsInNativeAnonymousSubtree()) {
|
||||
SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
|
||||
}
|
||||
if (aParent->HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
|
||||
if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
|
||||
SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
|
||||
}
|
||||
if (HasFlag(NODE_IS_ANONYMOUS_ROOT)) {
|
||||
aParent->SetMayHaveAnonymousChildren();
|
||||
aParent.SetMayHaveAnonymousChildren();
|
||||
}
|
||||
if (aParent->IsInShadowTree()) {
|
||||
ClearSubtreeRootPointer();
|
||||
SetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
MOZ_ASSERT(aParent->GetContainingShadow());
|
||||
ExtendedDOMSlots()->mContainingShadow = aParent->GetContainingShadow();
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(wasInShadowTree, IsInShadowTree());
|
||||
|
||||
// Now set the parent.
|
||||
if (aParent) {
|
||||
if (!GetParent()) {
|
||||
NS_ADDREF(aParent);
|
||||
mParent = &aParent;
|
||||
if (!hadParent && aParent.IsContent()) {
|
||||
SetParentIsContent(true);
|
||||
NS_ADDREF(mParent);
|
||||
}
|
||||
mParent = aParent;
|
||||
} else {
|
||||
mParent = aDocument;
|
||||
}
|
||||
SetParentIsContent(aParent);
|
||||
|
||||
// XXXbz sXBL/XBL2 issue!
|
||||
MOZ_ASSERT(!!GetParent() == aParent.IsContent());
|
||||
|
||||
MOZ_ASSERT(!HasAnyOfFlags(Element::kAllServoDescendantBits));
|
||||
|
||||
// Finally, set the document
|
||||
if (aDocument) {
|
||||
// Notify XBL- & nsIAnonymousContentCreator-generated
|
||||
// anonymous content that the document is changing.
|
||||
// XXXbz ordering issues here? Probably not, since ChangeDocumentFor is
|
||||
// just pretty broken anyway.... Need to get it working.
|
||||
// XXXbz XBL doesn't handle this (asserts), and we don't really want
|
||||
// to be doing this during parsing anyway... sort this out.
|
||||
// aDocument->BindingManager()->ChangeDocumentFor(this, nullptr,
|
||||
// aDocument);
|
||||
|
||||
if (aParent.IsInUncomposedDoc() || aParent.IsInShadowTree()) {
|
||||
// We no longer need to track the subtree pointer (and in fact we'll assert
|
||||
// if we do this any later).
|
||||
ClearSubtreeRootPointer();
|
||||
SetIsConnected(aParent.IsInComposedDoc());
|
||||
|
||||
// Being added to a document.
|
||||
if (aParent.IsInUncomposedDoc()) {
|
||||
SetIsInDocument();
|
||||
SetIsConnected(true);
|
||||
|
||||
} else {
|
||||
SetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
MOZ_ASSERT(aParent.IsContent() &&
|
||||
aParent.AsContent()->GetContainingShadow());
|
||||
ExtendedDOMSlots()->mContainingShadow =
|
||||
aParent.AsContent()->GetContainingShadow();
|
||||
}
|
||||
// Clear the lazy frame construction bits.
|
||||
UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
|
||||
} else if (IsInShadowTree()) {
|
||||
SetIsConnected(aParent->IsInComposedDoc());
|
||||
// We're not in a document, but we did get inserted into a shadow tree.
|
||||
// Since we won't have any restyle data in the document's restyle trackers,
|
||||
// don't let us get inserted with restyle bits set incorrectly.
|
||||
//
|
||||
// Also clear all the other flags that are cleared above when we do get
|
||||
// inserted into a document.
|
||||
//
|
||||
// See the comment about the restyle bits above, it also applies.
|
||||
UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
|
||||
} else {
|
||||
// If we're not in the doc and not in a shadow tree,
|
||||
// update our subtree pointer.
|
||||
SetSubtreeRootPointer(aParent->SubtreeRoot());
|
||||
SetSubtreeRootPointer(aParent.SubtreeRoot());
|
||||
}
|
||||
|
||||
if (IsInComposedDoc()) {
|
||||
// Connected callback must be enqueued whenever a custom element becomes
|
||||
// connected.
|
||||
CustomElementData* data = GetCustomElementData();
|
||||
if (data) {
|
||||
if (CustomElementData* data = GetCustomElementData()) {
|
||||
if (data->mState == CustomElementData::State::eCustom) {
|
||||
nsContentUtils::EnqueueLifecycleCallback(Document::eConnected, this);
|
||||
} else {
|
||||
@ -1718,7 +1681,7 @@ nsresult Element::BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
// because it has to happen after updating the parent pointer, but before
|
||||
// recursively binding the kids.
|
||||
if (IsHTMLElement()) {
|
||||
SetDirOnBind(this, aParent);
|
||||
SetDirOnBind(this, nsIContent::FromNode(aParent));
|
||||
}
|
||||
|
||||
UpdateEditableState(false);
|
||||
@ -1727,31 +1690,48 @@ nsresult Element::BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
// also need to be told that they are moving.
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
|
||||
nsXBLBinding* binding =
|
||||
OwnerDoc()->BindingManager()->GetBindingWithContent(this);
|
||||
aContext.OwnerDoc().BindingManager()->GetBindingWithContent(this);
|
||||
|
||||
if (binding) {
|
||||
binding->BindAnonymousContent(binding->GetAnonymousContent(), this);
|
||||
}
|
||||
}
|
||||
|
||||
// Now recurse into our kids
|
||||
// Call BindToTree on shadow root children.
|
||||
nsresult rv;
|
||||
for (nsIContent* child = GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
rv = child->BindToTree(aDocument, this, aBindingParent);
|
||||
if (ShadowRoot* shadowRoot = GetShadowRoot()) {
|
||||
rv = shadowRoot->Bind();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsNodeUtils::ParentChainChanged(this);
|
||||
if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
|
||||
nsNodeUtils::NativeAnonymousChildListChange(this, false);
|
||||
// Now recurse into our kids. Ensure this happens after binding the shadow
|
||||
// root so that directionality of slots is updated.
|
||||
{
|
||||
BindContext::NestingLevel level(aContext, *this);
|
||||
for (nsIContent* child = GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
rv = child->BindToTree(aContext, *this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we only add to the table once, in the case we move the ShadowRoot
|
||||
// around.
|
||||
if (HasID() && !wasInShadowTree) {
|
||||
MutationObservers::NotifyParentChainChanged(this);
|
||||
if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
|
||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, false);
|
||||
}
|
||||
|
||||
// Ensure we only run this once, in the case we move the ShadowRoot around.
|
||||
if (aContext.SubtreeRootChanges()) {
|
||||
if (HasPartAttribute()) {
|
||||
if (ShadowRoot* shadow = GetContainingShadow()) {
|
||||
shadow->PartAdded(*this);
|
||||
}
|
||||
}
|
||||
if (HasID()) {
|
||||
AddToIdTable(DoGetID());
|
||||
}
|
||||
HandleShadowDOMRelatedInsertionSteps(hadParent);
|
||||
}
|
||||
|
||||
if (MayHaveStyle() && !IsXULElement()) {
|
||||
// XXXbz if we already have a style attr parsed, this won't do
|
||||
@ -1760,25 +1740,19 @@ nsresult Element::BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(false, false);
|
||||
}
|
||||
|
||||
// Call BindToTree on shadow root children.
|
||||
if (ShadowRoot* shadowRoot = GetShadowRoot()) {
|
||||
rv = shadowRoot->Bind();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// FIXME(emilio): Why is this needed? The element shouldn't even be styled in
|
||||
// the first place, we should style it properly eventually.
|
||||
//
|
||||
// Also, if this _is_ needed, then it's wrong and should use GetComposedDoc()
|
||||
// to account for Shadow DOM.
|
||||
if (aDocument && MayHaveAnimations()) {
|
||||
if (aParent.IsInUncomposedDoc() && MayHaveAnimations()) {
|
||||
PseudoStyleType pseudoType = GetPseudoElementType();
|
||||
if ((pseudoType == PseudoStyleType::NotPseudo ||
|
||||
pseudoType == PseudoStyleType::before ||
|
||||
pseudoType == PseudoStyleType::after ||
|
||||
pseudoType == PseudoStyleType::marker) &&
|
||||
EffectSet::GetEffectSet(this, pseudoType)) {
|
||||
if (nsPresContext* presContext = aDocument->GetPresContext()) {
|
||||
if (nsPresContext* presContext = aContext.OwnerDoc().GetPresContext()) {
|
||||
presContext->EffectCompositor()->RequestRestyle(
|
||||
this, pseudoType, EffectCompositor::RestyleType::Standard,
|
||||
EffectCompositor::CascadeLevel::Animations);
|
||||
@ -1789,11 +1763,16 @@ nsresult Element::BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
// XXXbz script execution during binding can trigger some of these
|
||||
// postcondition asserts.... But we do want that, since things will
|
||||
// generally be quite broken when that happens.
|
||||
MOZ_ASSERT(aDocument == GetUncomposedDoc(), "Bound to wrong document");
|
||||
MOZ_ASSERT(aParent == GetParent(), "Bound to wrong parent");
|
||||
MOZ_ASSERT(aBindingParent == GetBindingParent(),
|
||||
MOZ_ASSERT(OwnerDoc() == aParent.OwnerDoc(), "Bound to wrong document");
|
||||
MOZ_ASSERT(IsInComposedDoc() == aContext.InComposedDoc());
|
||||
MOZ_ASSERT(IsInUncomposedDoc() == aContext.InUncomposedDoc());
|
||||
MOZ_ASSERT(&aParent == GetParentNode(), "Bound to wrong parent node");
|
||||
MOZ_ASSERT(aContext.GetBindingParent() == GetBindingParent(),
|
||||
"Bound to wrong binding parent");
|
||||
|
||||
MOZ_ASSERT(aParent.IsInUncomposedDoc() == IsInUncomposedDoc());
|
||||
MOZ_ASSERT(aParent.IsInComposedDoc() == IsInComposedDoc());
|
||||
MOZ_ASSERT(aParent.IsInShadowTree() == IsInShadowTree());
|
||||
MOZ_ASSERT(aParent.SubtreeRoot() == SubtreeRoot());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1823,30 +1802,31 @@ RemoveFromBindingManagerRunnable::Run() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool ShouldRemoveFromIdTableOnUnbind(const Element& aElement,
|
||||
bool aNullParent) {
|
||||
if (aElement.IsInUncomposedDoc()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!aElement.IsInShadowTree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return aNullParent || !aElement.GetParent()->IsInShadowTree();
|
||||
bool WillDetachFromShadowOnUnbind(const Element& aElement, bool aNullParent) {
|
||||
// If our parent still is in a shadow tree by now, and we're not removing
|
||||
// ourselves from it, then we're still going to be in a shadow tree after
|
||||
// this.
|
||||
return aElement.IsInShadowTree() &&
|
||||
(aNullParent || !aElement.GetParent()->IsInShadowTree());
|
||||
}
|
||||
|
||||
void Element::UnbindFromTree(bool aDeep, bool aNullParent) {
|
||||
MOZ_ASSERT(aDeep || (!GetUncomposedDoc() && !GetBindingParent()),
|
||||
"Shallow unbind won't clear document and binding parent on "
|
||||
"kids!");
|
||||
void Element::UnbindFromTree(bool aNullParent) {
|
||||
HandleShadowDOMRelatedRemovalSteps(aNullParent);
|
||||
|
||||
const bool detachingFromShadow =
|
||||
WillDetachFromShadowOnUnbind(*this, aNullParent);
|
||||
// Make sure to only remove from the ID table if our subtree root is actually
|
||||
// changing.
|
||||
if (ShouldRemoveFromIdTableOnUnbind(*this, aNullParent)) {
|
||||
if (IsInUncomposedDoc() || detachingFromShadow) {
|
||||
RemoveFromIdTable();
|
||||
}
|
||||
|
||||
if (detachingFromShadow && HasPartAttribute()) {
|
||||
if (ShadowRoot* shadow = GetContainingShadow()) {
|
||||
shadow->PartRemoved(*this);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to unbind this node before doing the kids
|
||||
Document* document = GetComposedDoc();
|
||||
|
||||
@ -1874,7 +1854,7 @@ void Element::UnbindFromTree(bool aDeep, bool aNullParent) {
|
||||
|
||||
if (aNullParent) {
|
||||
if (IsRootOfNativeAnonymousSubtree()) {
|
||||
nsNodeUtils::NativeAnonymousChildListChange(this, true);
|
||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, true);
|
||||
}
|
||||
|
||||
if (GetParent()) {
|
||||
@ -1931,6 +1911,12 @@ void Element::UnbindFromTree(bool aDeep, bool aNullParent) {
|
||||
|
||||
ClearInDocument();
|
||||
SetIsConnected(false);
|
||||
if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n()) {
|
||||
if (document) {
|
||||
document->mL10nProtoElements.Remove(this);
|
||||
}
|
||||
ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n();
|
||||
}
|
||||
|
||||
if (aNullParent || !mParent->IsInShadowTree()) {
|
||||
UnsetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
@ -1943,7 +1929,6 @@ void Element::UnbindFromTree(bool aDeep, bool aNullParent) {
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
if (nsXULElement* xulElem = nsXULElement::FromNode(this)) {
|
||||
;
|
||||
xulElem->SetXULBindingParent(nullptr);
|
||||
clearBindingParent = false;
|
||||
}
|
||||
@ -1996,17 +1981,15 @@ void Element::UnbindFromTree(bool aDeep, bool aNullParent) {
|
||||
ResetDir(this);
|
||||
}
|
||||
|
||||
if (aDeep) {
|
||||
for (nsIContent* child = GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
// Note that we pass false for aNullParent here, since we don't want
|
||||
// the kids to forget us. We _do_ want them to forget their binding
|
||||
// parent, though, since this only walks non-anonymous kids.
|
||||
child->UnbindFromTree(true, false);
|
||||
}
|
||||
child->UnbindFromTree(false);
|
||||
}
|
||||
|
||||
nsNodeUtils::ParentChainChanged(this);
|
||||
MutationObservers::NotifyParentChainChanged(this);
|
||||
|
||||
// Unbind children of shadow root.
|
||||
if (ShadowRoot* shadowRoot = GetShadowRoot()) {
|
||||
@ -2032,11 +2015,8 @@ DeclarationBlock* Element::GetSMILOverrideStyleDeclaration() {
|
||||
return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr;
|
||||
}
|
||||
|
||||
nsresult Element::SetSMILOverrideStyleDeclaration(
|
||||
DeclarationBlock* aDeclaration) {
|
||||
Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
|
||||
|
||||
slots->mSMILOverrideStyleDeclaration = aDeclaration;
|
||||
void Element::SetSMILOverrideStyleDeclaration(DeclarationBlock& aDeclaration) {
|
||||
ExtendedDOMSlots()->mSMILOverrideStyleDeclaration = &aDeclaration;
|
||||
|
||||
// Only need to request a restyle if we're in a document. (We might not
|
||||
// be in a document, if we're clearing animation effects on a target node
|
||||
@ -2046,8 +2026,6 @@ nsresult Element::SetSMILOverrideStyleDeclaration(
|
||||
presShell->RestyleForAnimation(this, StyleRestyleHint_RESTYLE_SMIL);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool Element::IsLabelable() const { return false; }
|
||||
@ -2223,13 +2201,13 @@ nsresult Element::LeaveLink(nsPresContext* aPresContext) {
|
||||
return nsDocShell::Cast(shell)->OnLeaveLink();
|
||||
}
|
||||
|
||||
nsresult Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
|
||||
void Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
|
||||
bool aDefer) {
|
||||
Document* ownerDoc = OwnerDoc();
|
||||
if (ownerDoc->IsLoadedAsData()) {
|
||||
// Make this a no-op rather than throwing an error to avoid
|
||||
// the error causing problems setting the attribute.
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aEventName, "Must have event name!");
|
||||
@ -2237,13 +2215,12 @@ nsresult Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
|
||||
EventListenerManager* manager =
|
||||
GetEventListenerManagerForAttr(aEventName, &defer);
|
||||
if (!manager) {
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
defer = defer && aDefer; // only defer if everyone agrees...
|
||||
manager->SetEventHandler(aEventName, aValue, defer,
|
||||
!nsContentUtils::IsChromeDoc(ownerDoc), this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@ -2333,7 +2310,8 @@ bool Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsAtom* aName,
|
||||
}
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
nsNodeUtils::AttributeSetToCurrentValue(this, aNamespaceID, aName);
|
||||
MutationObservers::NotifyAttributeSetToCurrentValue(this, aNamespaceID,
|
||||
aName);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2382,7 +2360,8 @@ nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
|
||||
}
|
||||
|
||||
if (aNotify) {
|
||||
nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
|
||||
MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName,
|
||||
modType);
|
||||
}
|
||||
|
||||
// Hold a script blocker while calling ParseAttribute since that can call
|
||||
@ -2428,7 +2407,8 @@ nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
|
||||
}
|
||||
|
||||
if (aNotify) {
|
||||
nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
|
||||
MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName,
|
||||
modType);
|
||||
}
|
||||
|
||||
nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
|
||||
@ -2509,6 +2489,15 @@ nsresult Element::SetAttrAndNotify(
|
||||
}
|
||||
}
|
||||
|
||||
if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n() &&
|
||||
aNamespaceID == kNameSpaceID_None &&
|
||||
(aName == nsGkAtoms::datal10nid || aName == nsGkAtoms::datal10nargs)) {
|
||||
ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n();
|
||||
if (aComposedDocument) {
|
||||
aComposedDocument->mL10nProtoElements.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
CustomElementDefinition* definition = GetCustomElementDefinition();
|
||||
// Only custom element which is in `custom` state could get the
|
||||
// CustomElementDefinition.
|
||||
@ -2553,7 +2542,7 @@ nsresult Element::SetAttrAndNotify(
|
||||
// Don't pass aOldValue to AttributeChanged since it may not be reliable.
|
||||
// Callers only compute aOldValue under certain conditions which may not
|
||||
// be triggered by all nsIMutationObservers.
|
||||
nsNodeUtils::AttributeChanged(
|
||||
MutationObservers::NotifyAttributeChanged(
|
||||
this, aNamespaceID, aName, aModType,
|
||||
aParsedValue.StoresOwnData() ? &aParsedValue : nullptr);
|
||||
}
|
||||
@ -2624,8 +2613,7 @@ nsresult Element::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
||||
const nsAttrValueOrString* aValue,
|
||||
bool aNotify) {
|
||||
if (aNamespaceID == kNameSpaceID_None) {
|
||||
if (aName == nsGkAtoms::_class) {
|
||||
if (aValue) {
|
||||
if (aName == nsGkAtoms::_class && aValue) {
|
||||
// Note: This flag is asymmetrical. It is never unset and isn't exact.
|
||||
// If it is ever made to be exact, we probably need to handle this
|
||||
// similarly to how ids are handled in PreIdMaybeChange and
|
||||
@ -2637,11 +2625,38 @@ nsresult Element::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
||||
SetMayHaveClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Element::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
const nsAttrValue* aOldValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal,
|
||||
bool aNotify) {
|
||||
if (aNamespaceID == kNameSpaceID_None) {
|
||||
if (aName == nsGkAtoms::part) {
|
||||
bool isPart = !!aValue;
|
||||
if (HasPartAttribute() != isPart) {
|
||||
SetHasPartAttribute(isPart);
|
||||
if (ShadowRoot* shadow = GetContainingShadow()) {
|
||||
if (isPart) {
|
||||
shadow->PartAdded(*this);
|
||||
} else {
|
||||
shadow->PartRemoved(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(HasPartAttribute() == isPart);
|
||||
} else if (aName == nsGkAtoms::slot && GetParent()) {
|
||||
if (ShadowRoot* shadow = GetParent()->GetShadowRoot()) {
|
||||
shadow->MaybeReassignElement(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Element::PreIdMaybeChange(int32_t aNamespaceID, nsAtom* aName,
|
||||
const nsAttrValueOrString* aValue) {
|
||||
if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
|
||||
@ -2732,8 +2747,8 @@ nsresult Element::UnsetAttr(int32_t aNameSpaceID, nsAtom* aName, bool aNotify) {
|
||||
mozAutoDocUpdate updateBatch(document, aNotify);
|
||||
|
||||
if (aNotify) {
|
||||
nsNodeUtils::AttributeWillChange(this, aNameSpaceID, aName,
|
||||
MutationEvent_Binding::REMOVAL);
|
||||
MutationObservers::NotifyAttributeWillChange(
|
||||
this, aNameSpaceID, aName, MutationEvent_Binding::REMOVAL);
|
||||
}
|
||||
|
||||
nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify);
|
||||
@ -2808,8 +2823,8 @@ nsresult Element::UnsetAttr(int32_t aNameSpaceID, nsAtom* aName, bool aNotify) {
|
||||
if (aNotify) {
|
||||
// We can always pass oldValue here since there is no new value which could
|
||||
// have corrupted it.
|
||||
nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
|
||||
MutationEvent_Binding::REMOVAL, &oldValue);
|
||||
MutationObservers::NotifyAttributeChanged(
|
||||
this, aNameSpaceID, aName, MutationEvent_Binding::REMOVAL, &oldValue);
|
||||
}
|
||||
|
||||
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
|
||||
@ -3032,7 +3047,7 @@ void Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor) {
|
||||
// Set the status bar similarly for mouseover and focus
|
||||
case eMouseOver:
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
MOZ_FALLTHROUGH;
|
||||
[[fallthrough]];
|
||||
case eFocus: {
|
||||
InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent();
|
||||
if (!focusEvent || !focusEvent->mIsRefocus) {
|
||||
@ -3047,7 +3062,7 @@ void Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor) {
|
||||
}
|
||||
case eMouseOut:
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
MOZ_FALLTHROUGH;
|
||||
[[fallthrough]];
|
||||
case eBlur: {
|
||||
nsresult rv = LeaveLink(aVisitor.mPresContext);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
@ -3214,19 +3229,39 @@ nsDOMTokenList* Element::GetTokenList(
|
||||
return list;
|
||||
}
|
||||
|
||||
nsresult Element::CopyInnerTo(Element* aDst) {
|
||||
nsresult Element::CopyInnerTo(Element* aDst, ReparseAttributes aReparse) {
|
||||
nsresult rv = aDst->mAttrs.EnsureCapacityToClone(mAttrs);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t i, count = mAttrs.AttrCount();
|
||||
for (i = 0; i < count; ++i) {
|
||||
const nsAttrName* name = mAttrs.AttrNameAt(i);
|
||||
const nsAttrValue* value = mAttrs.AttrAt(i);
|
||||
const bool reparse = aReparse == ReparseAttributes::Yes;
|
||||
|
||||
uint32_t count = mAttrs.AttrCount();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
BorrowedAttrInfo info = mAttrs.AttrInfoAt(i);
|
||||
const nsAttrName* name = info.mName;
|
||||
const nsAttrValue* value = info.mValue;
|
||||
if (value->Type() == nsAttrValue::eCSSDeclaration) {
|
||||
MOZ_ASSERT(name->Equals(nsGkAtoms::style, kNameSpaceID_None));
|
||||
// We still clone CSS attributes, even in the `reparse` (cross-document)
|
||||
// case. https://github.com/w3c/webappsec-csp/issues/212
|
||||
nsAttrValue valueCopy(*value);
|
||||
rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
|
||||
name->GetPrefix(), valueCopy, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
value->GetCSSDeclarationValue()->SetImmutable();
|
||||
} else if (reparse) {
|
||||
nsAutoString valStr;
|
||||
value->ToString(valStr);
|
||||
rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
|
||||
name->GetPrefix(), valStr, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
nsAttrValue valueCopy(*value);
|
||||
rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
|
||||
name->GetPrefix(), valueCopy, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -3287,11 +3322,7 @@ CORSMode Element::AttrValueToCORSMode(const nsAttrValue* aValue) {
|
||||
}
|
||||
|
||||
static const char* GetFullscreenError(CallerType aCallerType) {
|
||||
if (!nsContentUtils::IsRequestFullscreenAllowed(aCallerType)) {
|
||||
return "FullscreenDeniedNotInputDriven";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nsContentUtils::CheckRequestFullscreenAllowed(aCallerType);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
|
||||
@ -3470,7 +3501,7 @@ already_AddRefed<Animation> Element::Animate(
|
||||
return animation.forget();
|
||||
}
|
||||
|
||||
void Element::GetAnimations(const AnimationFilter& filter,
|
||||
void Element::GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations) {
|
||||
Document* doc = GetComposedDoc();
|
||||
if (doc) {
|
||||
@ -3501,7 +3532,7 @@ void Element::GetAnimations(const AnimationFilter& filter,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!filter.mSubtree || pseudoType == PseudoStyleType::before ||
|
||||
if (!aOptions.mSubtree || pseudoType == PseudoStyleType::before ||
|
||||
pseudoType == PseudoStyleType::after ||
|
||||
pseudoType == PseudoStyleType::marker) {
|
||||
GetAnimationsUnsorted(elem, pseudoType, aAnimations);
|
||||
@ -3755,8 +3786,9 @@ void Element::InsertAdjacentText(const nsAString& aWhere,
|
||||
}
|
||||
|
||||
TextEditor* Element::GetTextEditorInternal() {
|
||||
nsCOMPtr<nsITextControlElement> textCtrl = do_QueryInterface(this);
|
||||
return textCtrl ? textCtrl->GetTextEditor() : nullptr;
|
||||
TextControlElement* textControlElement = TextControlElement::FromNode(this);
|
||||
return textControlElement ? MOZ_KnownLive(textControlElement)->GetTextEditor()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
nsresult Element::SetBoolAttr(nsAtom* aAttr, bool aValue) {
|
||||
@ -3828,19 +3860,19 @@ float Element::FontSizeInflation() {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
net::ReferrerPolicy Element::GetReferrerPolicyAsEnum() {
|
||||
ReferrerPolicy Element::GetReferrerPolicyAsEnum() {
|
||||
if (IsHTMLElement()) {
|
||||
const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrerpolicy);
|
||||
return ReferrerPolicyFromAttr(referrerValue);
|
||||
}
|
||||
return net::RP_Unset;
|
||||
return ReferrerPolicy::_empty;
|
||||
}
|
||||
|
||||
net::ReferrerPolicy Element::ReferrerPolicyFromAttr(const nsAttrValue* aValue) {
|
||||
ReferrerPolicy Element::ReferrerPolicyFromAttr(const nsAttrValue* aValue) {
|
||||
if (aValue && aValue->Type() == nsAttrValue::eEnum) {
|
||||
return net::ReferrerPolicy(aValue->GetEnumValue());
|
||||
return ReferrerPolicy(aValue->GetEnumValue());
|
||||
}
|
||||
return net::RP_Unset;
|
||||
return ReferrerPolicy::_empty;
|
||||
}
|
||||
|
||||
already_AddRefed<nsDOMStringMap> Element::Dataset() {
|
||||
@ -4123,18 +4155,15 @@ void Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
|
||||
|
||||
// Now measure just the ComputedValues (and style structs) under
|
||||
// mServoData. This counts towards the relevant fields in |aSizes|.
|
||||
RefPtr<ComputedStyle> sc;
|
||||
if (Servo_Element_HasPrimaryComputedValues(this)) {
|
||||
sc = Servo_Element_GetPrimaryComputedValues(this).Consume();
|
||||
if (!aSizes.mState.HaveSeenPtr(sc.get())) {
|
||||
sc->AddSizeOfIncludingThis(aSizes, &aSizes.mLayoutComputedValuesDom);
|
||||
if (auto* style = Servo_Element_GetMaybeOutOfDateStyle(this)) {
|
||||
if (!aSizes.mState.HaveSeenPtr(style)) {
|
||||
style->AddSizeOfIncludingThis(aSizes, &aSizes.mLayoutComputedValuesDom);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < PseudoStyle::kEagerPseudoCount; i++) {
|
||||
if (Servo_Element_HasPseudoComputedValues(this, i)) {
|
||||
sc = Servo_Element_GetPseudoComputedValues(this, i).Consume();
|
||||
if (!aSizes.mState.HaveSeenPtr(sc.get())) {
|
||||
sc->AddSizeOfIncludingThis(aSizes,
|
||||
if (auto* style = Servo_Element_GetMaybeOutOfDatePseudoStyle(this, i)) {
|
||||
if (!aSizes.mState.HaveSeenPtr(style)) {
|
||||
style->AddSizeOfIncludingThis(aSizes,
|
||||
&aSizes.mLayoutComputedValuesDom);
|
||||
}
|
||||
}
|
||||
@ -4409,5 +4438,14 @@ double Element::FirstLineBoxBSize() const {
|
||||
: 0.0;
|
||||
}
|
||||
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
void Element::AssertInvariantsOnNodeInfoChange() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsInComposedDoc());
|
||||
if (nsCOMPtr<Link> link = do_QueryInterface(this)) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!link->HasPendingLinkUpdate());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -12,7 +12,6 @@
|
||||
#define mozilla_dom_Element_h__
|
||||
|
||||
#include "AttrArray.h"
|
||||
#include "DOMIntersectionObserver.h"
|
||||
#include "nsAttrValue.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsChangeHint.h"
|
||||
@ -20,7 +19,6 @@
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsINodeList.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "Units.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
@ -81,7 +79,7 @@ namespace css {
|
||||
struct URLValue;
|
||||
} // namespace css
|
||||
namespace dom {
|
||||
struct AnimationFilter;
|
||||
struct GetAnimationsOptions;
|
||||
struct ScrollIntoViewOptions;
|
||||
struct ScrollToOptions;
|
||||
class DOMIntersectionObserver;
|
||||
@ -351,7 +349,7 @@ class Element : public FragmentOrElement {
|
||||
* notify the document's pres context, so that the style changes will be
|
||||
* noticed.
|
||||
*/
|
||||
nsresult SetSMILOverrideStyleDeclaration(DeclarationBlock* aDeclaration);
|
||||
void SetSMILOverrideStyleDeclaration(DeclarationBlock&);
|
||||
|
||||
/**
|
||||
* Returns a new SMILAttr that allows the caller to animate the given
|
||||
@ -457,7 +455,7 @@ class Element : public FragmentOrElement {
|
||||
}
|
||||
}
|
||||
|
||||
bool GetBindingURL(Document* aDocument, css::URLValue** aResult);
|
||||
mozilla::StyleUrlOrNone GetBindingURL(Document* aDocument);
|
||||
|
||||
Directionality GetComputedDirectionality() const;
|
||||
|
||||
@ -650,10 +648,9 @@ class Element : public FragmentOrElement {
|
||||
|
||||
void UpdateEditableState(bool aNotify) override;
|
||||
|
||||
nsresult BindToTree(Document* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent) override;
|
||||
nsresult BindToTree(BindContext&, nsINode& aParent) override;
|
||||
|
||||
void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override;
|
||||
void UnbindFromTree(bool aNullParent = true) override;
|
||||
|
||||
/**
|
||||
* Normalizes an attribute name and returns it as a nodeinfo if an attribute
|
||||
@ -1336,7 +1333,7 @@ class Element : public FragmentOrElement {
|
||||
// Note: GetAnimations will flush style while GetAnimationsUnsorted won't.
|
||||
// Callers must keep this element alive because flushing style may destroy
|
||||
// this element.
|
||||
void GetAnimations(const AnimationFilter& filter,
|
||||
void GetAnimations(const GetAnimationsOptions& aOptions,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations);
|
||||
static void GetAnimationsUnsorted(Element* aElement,
|
||||
PseudoStyleType aPseudoType,
|
||||
@ -1360,7 +1357,7 @@ class Element : public FragmentOrElement {
|
||||
* @param aValue the JS to attach
|
||||
* @param aDefer indicates if deferred execution is allowed
|
||||
*/
|
||||
nsresult SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
|
||||
void SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
|
||||
bool aDefer = true);
|
||||
|
||||
/**
|
||||
@ -1404,6 +1401,14 @@ class Element : public FragmentOrElement {
|
||||
return HasServoData() && Servo_Element_IsDisplayContents(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* https://html.spec.whatwg.org/#being-rendered
|
||||
*
|
||||
* With a gotcha for display contents:
|
||||
* https://github.com/whatwg/html/issues/1837
|
||||
*/
|
||||
bool IsRendered() const { return GetPrimaryFrame() || IsDisplayContents(); }
|
||||
|
||||
const nsAttrValue* GetParsedAttr(const nsAtom* aAttr) const {
|
||||
return mAttrs.GetAttr(aAttr);
|
||||
}
|
||||
@ -1459,7 +1464,11 @@ class Element : public FragmentOrElement {
|
||||
*
|
||||
* If you change this, change also the similar method in Link.
|
||||
*/
|
||||
virtual void NodeInfoChanged(Document* aOldDoc) {}
|
||||
virtual void NodeInfoChanged(Document* aOldDoc) {
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
AssertInvariantsOnNodeInfoChange();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string into an nsAttrValue for a CORS attribute. This
|
||||
@ -1486,7 +1495,7 @@ class Element : public FragmentOrElement {
|
||||
/**
|
||||
* Locate a TextEditor rooted at this content node, if there is one.
|
||||
*/
|
||||
mozilla::TextEditor* GetTextEditorInternal();
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::TextEditor* GetTextEditorInternal();
|
||||
|
||||
/**
|
||||
* Gets value of boolean attribute. Only works for attributes in null
|
||||
@ -1579,8 +1588,8 @@ class Element : public FragmentOrElement {
|
||||
*/
|
||||
float FontSizeInflation();
|
||||
|
||||
net::ReferrerPolicy GetReferrerPolicyAsEnum();
|
||||
net::ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue);
|
||||
ReferrerPolicy GetReferrerPolicyAsEnum();
|
||||
ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue);
|
||||
|
||||
/*
|
||||
* Helpers for .dataset. This is implemented on Element, though only some
|
||||
@ -1768,15 +1777,11 @@ class Element : public FragmentOrElement {
|
||||
* principal is directly responsible for the attribute change.
|
||||
* @param aNotify Whether we plan to notify document observers.
|
||||
*/
|
||||
// Note that this is inlined so that when subclasses call it it gets
|
||||
// inlined. Those calls don't go through a vtable.
|
||||
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
const nsAttrValue* aOldValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal,
|
||||
bool aNotify) {
|
||||
return NS_OK;
|
||||
}
|
||||
bool aNotify);
|
||||
|
||||
/**
|
||||
* This function shall be called just before the id attribute changes. It will
|
||||
@ -1905,13 +1910,19 @@ class Element : public FragmentOrElement {
|
||||
nsAtom* aAtom,
|
||||
const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr);
|
||||
|
||||
enum class ReparseAttributes { No, Yes };
|
||||
/**
|
||||
* Copy attributes and state to another element
|
||||
* @param aDest the object to copy to
|
||||
*/
|
||||
nsresult CopyInnerTo(Element* aDest);
|
||||
nsresult CopyInnerTo(Element* aDest,
|
||||
ReparseAttributes = ReparseAttributes::Yes);
|
||||
|
||||
private:
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
void AssertInvariantsOnNodeInfoChange();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Slow path for GetClasses, this should only be called for SVG elements.
|
||||
*/
|
||||
|
@ -24,23 +24,21 @@ inline void Element::UnregisterActivityObserver() {
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
inline Element* nsINode::GetFlattenedTreeParentElement() const {
|
||||
inline mozilla::dom::Element* nsINode::GetFlattenedTreeParentElement() const {
|
||||
nsINode* parentNode = GetFlattenedTreeParentNode();
|
||||
if
|
||||
MOZ_LIKELY(parentNode && parentNode->IsElement()) {
|
||||
if MOZ_LIKELY (parentNode && parentNode->IsElement()) {
|
||||
return parentNode->AsElement();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline Element* nsINode::GetFlattenedTreeParentElementForStyle() const {
|
||||
inline mozilla::dom::Element* nsINode::GetFlattenedTreeParentElementForStyle()
|
||||
const {
|
||||
nsINode* parentNode = GetFlattenedTreeParentNodeForStyle();
|
||||
if
|
||||
MOZ_LIKELY(parentNode && parentNode->IsElement()) {
|
||||
if (MOZ_LIKELY(parentNode && parentNode->IsElement())) {
|
||||
return parentNode->AsElement();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include "nsIThreadRetargetableRequest.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "xpcpublic.h"
|
||||
@ -112,8 +111,7 @@ class EventSourceImpl final : public nsIObserver,
|
||||
static void TimerCallback(nsITimer* aTimer, void* aClosure);
|
||||
|
||||
nsresult PrintErrorOnConsole(const char* aBundleURI, const char* aError,
|
||||
const char16_t** aFormatStrings,
|
||||
uint32_t aFormatStringsLen);
|
||||
const nsTArray<nsString>& aFormatStrings);
|
||||
nsresult ConsoleError();
|
||||
|
||||
static nsresult StreamReaderFunc(nsIInputStream* aInputStream, void* aClosure,
|
||||
@ -794,11 +792,7 @@ EventSourceImpl::AsyncOnChannelRedirect(
|
||||
rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isValidScheme =
|
||||
(NS_SUCCEEDED(newURI->SchemeIs("http", &isValidScheme)) &&
|
||||
isValidScheme) ||
|
||||
(NS_SUCCEEDED(newURI->SchemeIs("https", &isValidScheme)) &&
|
||||
isValidScheme);
|
||||
bool isValidScheme = newURI->SchemeIs("http") || newURI->SchemeIs("https");
|
||||
|
||||
rv = mEventSource->CheckCurrentGlobalCorrectness();
|
||||
if (NS_FAILED(rv) || !isValidScheme) {
|
||||
@ -937,8 +931,8 @@ nsresult EventSourceImpl::SetupReferrerInfo() {
|
||||
MOZ_ASSERT(!IsShutDown());
|
||||
nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent();
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo =
|
||||
new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
|
||||
referrerInfo->InitWithDocument(doc);
|
||||
nsresult rv = mHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
@ -952,9 +946,7 @@ nsresult EventSourceImpl::InitChannelAndRequestEventSource() {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
bool isValidScheme =
|
||||
(NS_SUCCEEDED(mSrc->SchemeIs("http", &isValidScheme)) && isValidScheme) ||
|
||||
(NS_SUCCEEDED(mSrc->SchemeIs("https", &isValidScheme)) && isValidScheme);
|
||||
bool isValidScheme = mSrc->SchemeIs("http") || mSrc->SchemeIs("https");
|
||||
|
||||
nsresult rv = mEventSource->CheckCurrentGlobalCorrectness();
|
||||
if (NS_FAILED(rv) || !isValidScheme) {
|
||||
@ -1158,10 +1150,9 @@ nsresult EventSourceImpl::SetReconnectionTimeout() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult EventSourceImpl::PrintErrorOnConsole(const char* aBundleURI,
|
||||
const char* aError,
|
||||
const char16_t** aFormatStrings,
|
||||
uint32_t aFormatStringsLen) {
|
||||
nsresult EventSourceImpl::PrintErrorOnConsole(
|
||||
const char* aBundleURI, const char* aError,
|
||||
const nsTArray<nsString>& aFormatStrings) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!IsShutDown());
|
||||
nsCOMPtr<nsIStringBundleService> bundleService =
|
||||
@ -1183,9 +1174,8 @@ nsresult EventSourceImpl::PrintErrorOnConsole(const char* aBundleURI,
|
||||
|
||||
// Localize the error message
|
||||
nsAutoString message;
|
||||
if (aFormatStrings) {
|
||||
rv = strBundle->FormatStringFromName(aError, aFormatStrings,
|
||||
aFormatStringsLen, message);
|
||||
if (!aFormatStrings.IsEmpty()) {
|
||||
rv = strBundle->FormatStringFromName(aError, aFormatStrings, message);
|
||||
} else {
|
||||
rv = strBundle->GetStringFromName(aError, message);
|
||||
}
|
||||
@ -1210,17 +1200,15 @@ nsresult EventSourceImpl::ConsoleError() {
|
||||
nsresult rv = mSrc->GetSpec(targetSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
|
||||
const char16_t* formatStrings[] = {specUTF16.get()};
|
||||
AutoTArray<nsString, 1> formatStrings;
|
||||
CopyUTF8toUTF16(targetSpec, *formatStrings.AppendElement());
|
||||
|
||||
if (ReadyState() == CONNECTING) {
|
||||
rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
|
||||
"connectionFailure", formatStrings,
|
||||
ArrayLength(formatStrings));
|
||||
"connectionFailure", formatStrings);
|
||||
} else {
|
||||
rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
|
||||
"netInterrupt", formatStrings,
|
||||
ArrayLength(formatStrings));
|
||||
"netInterrupt", formatStrings);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -14,12 +14,6 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsDeque.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
@ -14,10 +14,19 @@
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
FormData::FormData(nsISupports* aOwner)
|
||||
: HTMLFormSubmission(nullptr, EmptyString(), UTF_8_ENCODING, nullptr),
|
||||
FormData::FormData(nsISupports* aOwner, NotNull<const Encoding*> aEncoding,
|
||||
Element* aOriginatingElement)
|
||||
: HTMLFormSubmission(nullptr, EmptyString(), aEncoding,
|
||||
aOriginatingElement),
|
||||
mOwner(aOwner) {}
|
||||
|
||||
FormData::FormData(const FormData& aFormData)
|
||||
: HTMLFormSubmission(aFormData.mActionURL, aFormData.mTarget,
|
||||
aFormData.mEncoding, aFormData.mOriginatingElement) {
|
||||
mOwner = aFormData.mOwner;
|
||||
mFormData = aFormData.mFormData;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
already_AddRefed<File> GetOrCreateFileCalledBlob(Blob& aBlob,
|
||||
@ -288,8 +297,18 @@ already_AddRefed<FormData> FormData::Constructor(
|
||||
const Optional<NonNull<HTMLFormElement> >& aFormElement, ErrorResult& aRv) {
|
||||
RefPtr<FormData> formData = new FormData(aGlobal.GetAsSupports());
|
||||
if (aFormElement.WasPassed()) {
|
||||
aRv = aFormElement.Value().WalkFormElements(formData);
|
||||
aRv = aFormElement.Value().ConstructEntryList(formData);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 9. Return a shallow clone of entry list.
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set
|
||||
if (StaticPrefs::dom_formdata_event_enabled()) {
|
||||
formData = formData->Clone();
|
||||
}
|
||||
}
|
||||
|
||||
return formData.forget();
|
||||
}
|
||||
|
||||
@ -300,23 +319,8 @@ nsresult FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
|
||||
nsACString& aContentTypeWithCharset,
|
||||
nsACString& aCharset) const {
|
||||
FSMultipartFormData fs(nullptr, EmptyString(), UTF_8_ENCODING, nullptr);
|
||||
|
||||
for (uint32_t i = 0; i < mFormData.Length(); ++i) {
|
||||
if (mFormData[i].wasNullBlob) {
|
||||
MOZ_ASSERT(mFormData[i].value.IsUSVString());
|
||||
fs.AddNameBlobOrNullPair(mFormData[i].name, nullptr);
|
||||
} else if (mFormData[i].value.IsUSVString()) {
|
||||
fs.AddNameValuePair(mFormData[i].name,
|
||||
mFormData[i].value.GetAsUSVString());
|
||||
} else if (mFormData[i].value.IsBlob()) {
|
||||
fs.AddNameBlobOrNullPair(mFormData[i].name,
|
||||
mFormData[i].value.GetAsBlob());
|
||||
} else {
|
||||
MOZ_ASSERT(mFormData[i].value.IsDirectory());
|
||||
fs.AddNameDirectoryPair(mFormData[i].name,
|
||||
mFormData[i].value.GetAsDirectory());
|
||||
}
|
||||
}
|
||||
nsresult rv = CopySubmissionDataTo(&fs);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
fs.GetContentType(aContentTypeWithCharset);
|
||||
aCharset.Truncate();
|
||||
@ -325,3 +329,31 @@ nsresult FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<FormData> FormData::Clone() {
|
||||
RefPtr<FormData> formData = new FormData(*this);
|
||||
return formData.forget();
|
||||
}
|
||||
|
||||
nsresult FormData::CopySubmissionDataTo(
|
||||
HTMLFormSubmission* aFormSubmission) const {
|
||||
MOZ_ASSERT(aFormSubmission, "Must have FormSubmission!");
|
||||
for (size_t i = 0; i < mFormData.Length(); ++i) {
|
||||
if (mFormData[i].wasNullBlob) {
|
||||
MOZ_ASSERT(mFormData[i].value.IsUSVString());
|
||||
aFormSubmission->AddNameBlobOrNullPair(mFormData[i].name, nullptr);
|
||||
} else if (mFormData[i].value.IsUSVString()) {
|
||||
aFormSubmission->AddNameValuePair(mFormData[i].name,
|
||||
mFormData[i].value.GetAsUSVString());
|
||||
} else if (mFormData[i].value.IsBlob()) {
|
||||
aFormSubmission->AddNameBlobOrNullPair(mFormData[i].name,
|
||||
mFormData[i].value.GetAsBlob());
|
||||
} else {
|
||||
MOZ_ASSERT(mFormData[i].value.IsDirectory());
|
||||
aFormSubmission->AddNameDirectoryPair(
|
||||
mFormData[i].name, mFormData[i].value.GetAsDirectory());
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ class FormData final : public nsISupports,
|
||||
public HTMLFormSubmission,
|
||||
public nsWrapperCache {
|
||||
private:
|
||||
FormData(const FormData& aFormData);
|
||||
~FormData() {}
|
||||
|
||||
struct FormDataTuple {
|
||||
@ -47,7 +48,11 @@ class FormData final : public nsISupports,
|
||||
Directory* aDirectory);
|
||||
|
||||
public:
|
||||
explicit FormData(nsISupports* aOwner = nullptr);
|
||||
explicit FormData(nsISupports* aOwner = nullptr,
|
||||
NotNull<const Encoding*> aEncoding = UTF_8_ENCODING,
|
||||
Element* aOriginatingElement = nullptr);
|
||||
|
||||
already_AddRefed<FormData> Clone();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FormData)
|
||||
@ -134,6 +139,8 @@ class FormData final : public nsISupports,
|
||||
nsACString& aContentTypeWithCharset,
|
||||
nsACString& aCharset) const;
|
||||
|
||||
nsresult CopySubmissionDataTo(HTMLFormSubmission* aFormSubmission) const;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include "nsIDocumentEncoder.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
@ -46,7 +45,6 @@
|
||||
#include "nsString.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsDOMCID.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsDOMCSSAttrDeclaration.h"
|
||||
#include "nsNameSpaceManager.h"
|
||||
#include "nsContentList.h"
|
||||
@ -54,10 +52,8 @@
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
#include "nsError.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "mozilla/InternalMutationEvent.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "nsAttrValueOrString.h"
|
||||
#include "nsQueryObject.h"
|
||||
#ifdef MOZ_XUL
|
||||
@ -81,16 +77,11 @@
|
||||
#include "nsContentCID.h"
|
||||
#include "nsWindowSizes.h"
|
||||
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsIControllers.h"
|
||||
#include "nsView.h"
|
||||
#include "nsViewManager.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
@ -107,7 +98,6 @@
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsCycleCollector.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
#include "mozilla/CORSMode.h"
|
||||
@ -157,7 +147,7 @@ NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsIContent)
|
||||
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
|
||||
nsIContent, nsNodeUtils::LastRelease(this))
|
||||
nsIContent, LastRelease())
|
||||
|
||||
nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
|
||||
// This handles also nested native anonymous content.
|
||||
@ -326,19 +316,14 @@ nsAtom* nsIContent::GetLang() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI> nsIContent::GetBaseURI(
|
||||
bool aTryUseXHRDocBaseURI) const {
|
||||
nsIURI* nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
|
||||
if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
|
||||
if (URLExtraData* data = use->GetContentURLData()) {
|
||||
return do_AddRef(data->BaseURI());
|
||||
return data->BaseURI();
|
||||
}
|
||||
}
|
||||
|
||||
Document* doc = OwnerDoc();
|
||||
// Start with document base
|
||||
nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
|
||||
|
||||
return base.forget();
|
||||
return OwnerDoc()->GetBaseURI(aTryUseXHRDocBaseURI);
|
||||
}
|
||||
|
||||
nsIURI* nsIContent::GetBaseURIForStyleAttr() const {
|
||||
@ -361,9 +346,10 @@ already_AddRefed<URLExtraData> nsIContent::GetURLDataForStyleAttr(
|
||||
}
|
||||
if (aSubjectPrincipal && aSubjectPrincipal != NodePrincipal()) {
|
||||
// TODO: Cache this?
|
||||
return MakeAndAddRef<URLExtraData>(
|
||||
OwnerDoc()->GetDocBaseURI(), OwnerDoc()->GetDocumentURI(),
|
||||
aSubjectPrincipal, OwnerDoc()->GetReferrerPolicy());
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo =
|
||||
ReferrerInfo::CreateForInternalCSSResources(OwnerDoc());
|
||||
return MakeAndAddRef<URLExtraData>(OwnerDoc()->GetDocBaseURI(),
|
||||
referrerInfo, aSubjectPrincipal);
|
||||
}
|
||||
// This also ignores the case that SVG inside XBL binding.
|
||||
// But it is probably fine.
|
||||
@ -391,13 +377,9 @@ static bool NeedsScriptTraverse(nsINode* aNode) {
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList)
|
||||
|
||||
// If nsAttrChildContentList is changed so that any additional fields are
|
||||
// traversed by the cycle collector, then CAN_SKIP must be updated to
|
||||
// check that the additional fields are null.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsAttrChildContentList)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAttrChildContentList, mNode)
|
||||
|
||||
// nsAttrChildContentList only ever has a single child, its wrapper, so if
|
||||
// the wrapper is known-live, the list can't be part of a garbage cycle.
|
||||
// If the wrapper is known-live, the list can't be part of a garbage cycle.
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList)
|
||||
return tmp->HasKnownLiveWrapper();
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
|
||||
@ -406,7 +388,6 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList)
|
||||
return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
|
||||
|
||||
// CanSkipThis returns false to avoid problems with incomplete unlinking.
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList)
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
|
||||
|
||||
@ -768,7 +749,7 @@ static nsINode* FindChromeAccessOnlySubtreeOwner(nsINode* aNode) {
|
||||
aNode = aNode->GetParentNode();
|
||||
}
|
||||
|
||||
return aNode ? aNode->GetParentOrHostNode() : nullptr;
|
||||
return aNode ? aNode->GetParentOrShadowHostNode() : nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsINode> FindChromeAccessOnlySubtreeOwner(
|
||||
@ -999,8 +980,8 @@ void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
|
||||
// dispatching event to Window object in a content page and
|
||||
// propagating the event to a chrome Element.
|
||||
if (targetInKnownToBeHandledScope &&
|
||||
nsContentUtils::ContentIsShadowIncludingDescendantOf(
|
||||
this, targetInKnownToBeHandledScope->SubtreeRoot())) {
|
||||
IsShadowIncludingInclusiveDescendantOf(
|
||||
targetInKnownToBeHandledScope->SubtreeRoot())) {
|
||||
// Part of step 11.4.
|
||||
// "If target's root is a shadow-including inclusive ancestor of
|
||||
// parent, then"
|
||||
@ -1120,6 +1101,28 @@ void nsIContent::SetXBLInsertionPoint(nsIContent* aContent) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void nsIContent::AssertAnonymousSubtreeRelatedInvariants() const {
|
||||
NS_ASSERTION(!IsRootOfNativeAnonymousSubtree() ||
|
||||
(GetParent() && GetBindingParent() == GetParent()),
|
||||
"root of native anonymous subtree must have parent equal "
|
||||
"to binding parent");
|
||||
NS_ASSERTION(!GetParent() || !IsInComposedDoc() ||
|
||||
((GetBindingParent() == GetParent()) ==
|
||||
HasFlag(NODE_IS_ANONYMOUS_ROOT)) ||
|
||||
// Unfortunately default content for XBL insertion points
|
||||
// is anonymous content that is bound with the parent of
|
||||
// the insertion point as the parent but the bound element
|
||||
// for the binding as the binding parent. So we have to
|
||||
// complicate the assert a bit here.
|
||||
(GetBindingParent() &&
|
||||
(GetBindingParent() == GetParent()->GetBindingParent()) ==
|
||||
HasFlag(NODE_IS_ANONYMOUS_ROOT)),
|
||||
"For connected nodes, flag and GetBindingParent() check "
|
||||
"should match");
|
||||
}
|
||||
#endif
|
||||
|
||||
void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
|
||||
OOMReporter& aError) {
|
||||
if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
|
||||
@ -2002,7 +2005,7 @@ void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
|
||||
ErrorResult& aError) {
|
||||
FragmentOrElement* target = this;
|
||||
// Handle template case.
|
||||
if (nsNodeUtils::IsTemplateElement(target)) {
|
||||
if (target->IsTemplateElement()) {
|
||||
DocumentFragment* frag =
|
||||
static_cast<HTMLTemplateElement*>(target)->Content();
|
||||
MOZ_ASSERT(frag);
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
|
||||
#include "nsIContent.h" // base class
|
||||
#include "nsNodeUtils.h" // class member nsNodeUtils::CloneNodeImpl
|
||||
#include "nsIHTMLCollection.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsXBLBinding.h"
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "mozIDOMWindow.h"
|
||||
#include "mozilla/dom/KeyboardEvent.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "mozilla/Encoding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIReferrerInfo.h"
|
||||
#include "nsBindingManager.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
@ -31,9 +32,8 @@ static DocumentOrShadowRoot* DocOrShadowFromContent(nsIContent& aContent) {
|
||||
}
|
||||
|
||||
void IDTracker::ResetToURIFragmentID(nsIContent* aFromContent, nsIURI* aURI,
|
||||
nsIURI* aReferrer,
|
||||
uint32_t aReferrerPolicy, bool aWatch,
|
||||
bool aReferenceImage) {
|
||||
nsIReferrerInfo* aReferrerInfo,
|
||||
bool aWatch, bool aReferenceImage) {
|
||||
MOZ_ASSERT(aFromContent,
|
||||
"ResetToURIFragmentID() expects non-null content pointer");
|
||||
|
||||
@ -110,8 +110,8 @@ void IDTracker::ResetToURIFragmentID(nsIContent* aFromContent, nsIURI* aURI,
|
||||
rv = aURI->EqualsExceptRef(doc->GetDocumentURI(), &isEqualExceptRef);
|
||||
if (NS_FAILED(rv) || !isEqualExceptRef) {
|
||||
RefPtr<Document::ExternalResourceLoad> load;
|
||||
doc = doc->RequestExternalResource(aURI, aReferrer, aReferrerPolicy,
|
||||
aFromContent, getter_AddRefs(load));
|
||||
doc = doc->RequestExternalResource(aURI, aReferrerInfo, aFromContent,
|
||||
getter_AddRefs(load));
|
||||
docOrShadow = doc;
|
||||
if (!doc) {
|
||||
if (!load || !aWatch) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
class nsIURI;
|
||||
class nsIReferrerInfo;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -53,16 +54,15 @@ class IDTracker {
|
||||
* do not trigger ElementChanged.
|
||||
* @param aFrom the source element for context
|
||||
* @param aURI the URI containing a hash-reference to the element
|
||||
* @param aReferrer the referrer URI for loading external resource
|
||||
* @param aReferrerPolicy the referrer policy for loading external resource
|
||||
* @param aReferrerInfo the referrerInfo for loading external resource
|
||||
* @param aWatch if false, then we do not set up the notifications to track
|
||||
* changes, so ElementChanged won't fire and get() will always return the same
|
||||
* value, the current element for the ID.
|
||||
* @param aReferenceImage whether the ID references image elements which are
|
||||
* subject to the document's mozSetImageElement overriding mechanism.
|
||||
*/
|
||||
void ResetToURIFragmentID(nsIContent* aFrom, nsIURI* aURI, nsIURI* aReferrer,
|
||||
uint32_t aReferrerPolicy, bool aWatch = true,
|
||||
void ResetToURIFragmentID(nsIContent* aFrom, nsIURI* aURI,
|
||||
nsIReferrerInfo* aReferrerInfo, bool aWatch = true,
|
||||
bool aReferenceImage = false);
|
||||
|
||||
/**
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/dom/TreeOrderedArray.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAtom.h"
|
||||
@ -23,6 +22,7 @@
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
class nsIContent;
|
||||
class nsINode;
|
||||
class nsContentList;
|
||||
class nsBaseContentList;
|
||||
|
||||
@ -47,7 +47,6 @@ class Element;
|
||||
class IdentifierMapEntry : public PLDHashEntryHdr {
|
||||
typedef dom::Document Document;
|
||||
typedef dom::Element Element;
|
||||
typedef net::ReferrerPolicy ReferrerPolicy;
|
||||
|
||||
/**
|
||||
* @see Document::IDTargetObserver, this is just here to avoid include hell.
|
||||
@ -56,24 +55,24 @@ class IdentifierMapEntry : public PLDHashEntryHdr {
|
||||
void* aData);
|
||||
|
||||
public:
|
||||
struct AtomOrString {
|
||||
MOZ_IMPLICIT AtomOrString(nsAtom* aAtom) : mAtom(aAtom) {}
|
||||
MOZ_IMPLICIT AtomOrString(const nsAString& aString) : mString(aString) {}
|
||||
AtomOrString(const AtomOrString& aOther)
|
||||
// We use DependentAtomOrString as our external key interface. This allows
|
||||
// consumers to use an nsAString, for example, without forcing a copy.
|
||||
struct DependentAtomOrString final {
|
||||
MOZ_IMPLICIT DependentAtomOrString(nsAtom* aAtom)
|
||||
: mAtom(aAtom), mString(nullptr) {}
|
||||
MOZ_IMPLICIT DependentAtomOrString(const nsAString& aString)
|
||||
: mAtom(nullptr), mString(&aString) {}
|
||||
DependentAtomOrString(const DependentAtomOrString& aOther)
|
||||
: mAtom(aOther.mAtom), mString(aOther.mString) {}
|
||||
|
||||
AtomOrString(AtomOrString&& aOther)
|
||||
: mAtom(aOther.mAtom.forget()), mString(aOther.mString) {}
|
||||
|
||||
RefPtr<nsAtom> mAtom;
|
||||
const nsString mString;
|
||||
nsAtom* mAtom;
|
||||
const nsAString* mString;
|
||||
};
|
||||
|
||||
typedef const AtomOrString& KeyType;
|
||||
typedef const AtomOrString* KeyTypePointer;
|
||||
typedef const DependentAtomOrString& KeyType;
|
||||
typedef const DependentAtomOrString* KeyTypePointer;
|
||||
|
||||
explicit IdentifierMapEntry(const AtomOrString& aKey);
|
||||
explicit IdentifierMapEntry(const AtomOrString* aKey);
|
||||
explicit IdentifierMapEntry(const DependentAtomOrString* aKey);
|
||||
IdentifierMapEntry(IdentifierMapEntry&& aOther);
|
||||
~IdentifierMapEntry();
|
||||
|
||||
@ -91,20 +90,20 @@ class IdentifierMapEntry : public PLDHashEntryHdr {
|
||||
return mKey.mAtom == aOtherKey->mAtom;
|
||||
}
|
||||
|
||||
return mKey.mAtom->Equals(aOtherKey->mString);
|
||||
return mKey.mAtom->Equals(*aOtherKey->mString);
|
||||
}
|
||||
|
||||
if (aOtherKey->mAtom) {
|
||||
return aOtherKey->mAtom->Equals(mKey.mString);
|
||||
}
|
||||
|
||||
return mKey.mString.Equals(aOtherKey->mString);
|
||||
return mKey.mString.Equals(*aOtherKey->mString);
|
||||
}
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
|
||||
|
||||
static PLDHashNumber HashKey(const KeyTypePointer aKey) {
|
||||
return aKey->mAtom ? aKey->mAtom->hash() : HashString(aKey->mString);
|
||||
return aKey->mAtom ? aKey->mAtom->hash() : HashString(*aKey->mString);
|
||||
}
|
||||
|
||||
enum { ALLOW_MEMMOVE = false };
|
||||
@ -155,6 +154,11 @@ class IdentifierMapEntry : public PLDHashEntryHdr {
|
||||
void RemoveContentChangeCallback(IDTargetObserver aCallback, void* aData,
|
||||
bool aForImage);
|
||||
|
||||
/**
|
||||
* Remove all elements and notify change listeners.
|
||||
*/
|
||||
void ClearAndNotify();
|
||||
|
||||
void Traverse(nsCycleCollectionTraversalCallback* aCallback);
|
||||
|
||||
struct ChangeCallback {
|
||||
@ -189,13 +193,35 @@ class IdentifierMapEntry : public PLDHashEntryHdr {
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
private:
|
||||
// We use an OwningAtomOrString as our internal key storage. It needs to own
|
||||
// the key string, whether in atom or string form.
|
||||
struct OwningAtomOrString final {
|
||||
OwningAtomOrString(const OwningAtomOrString& aOther)
|
||||
: mAtom(aOther.mAtom), mString(aOther.mString) {}
|
||||
|
||||
OwningAtomOrString(OwningAtomOrString&& aOther)
|
||||
: mAtom(std::move(aOther.mAtom)), mString(std::move(aOther.mString)) {}
|
||||
|
||||
explicit OwningAtomOrString(const DependentAtomOrString& aOther)
|
||||
// aOther may have a null mString, so jump through a bit of a hoop in
|
||||
// that case. I wish there were a way to just default-initialize
|
||||
// mString in that situation... We could also make mString not const
|
||||
// and only assign to it if aOther.mString is not null, but having it be
|
||||
// const is nice.
|
||||
: mAtom(aOther.mAtom),
|
||||
mString(aOther.mString ? *aOther.mString : EmptyString()) {}
|
||||
|
||||
RefPtr<nsAtom> mAtom;
|
||||
const nsString mString;
|
||||
};
|
||||
|
||||
IdentifierMapEntry(const IdentifierMapEntry& aOther) = delete;
|
||||
IdentifierMapEntry& operator=(const IdentifierMapEntry& aOther) = delete;
|
||||
|
||||
void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
|
||||
bool aImageOnly = false);
|
||||
|
||||
AtomOrString mKey;
|
||||
OwningAtomOrString mKey;
|
||||
dom::TreeOrderedArray<Element> mIdContentList;
|
||||
RefPtr<nsBaseContentList> mNameContentList;
|
||||
nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsICancelableRunnable.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "nsThreadPool.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "YCbCrUtils.h"
|
||||
@ -104,6 +104,8 @@ class EncodingCompleteEvent : public CancelableRunnable {
|
||||
MOZ_ASSERT(blob);
|
||||
|
||||
rv = callback->ReceiveBlob(blob.forget());
|
||||
} else {
|
||||
rv = callback->ReceiveBlob(nullptr);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -211,8 +213,6 @@ class EncodingRunnable : public Runnable {
|
||||
bool mUsingCustomOptions;
|
||||
};
|
||||
|
||||
StaticRefPtr<nsIThreadPool> ImageEncoder::sThreadPool;
|
||||
|
||||
/* static */
|
||||
nsresult ImageEncoder::ExtractData(nsAString& aType, const nsAString& aOptions,
|
||||
const nsIntSize aSize, bool aUsePlaceholder,
|
||||
@ -239,11 +239,6 @@ nsresult ImageEncoder::ExtractDataFromLayersImageAsync(
|
||||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||
}
|
||||
|
||||
nsresult rv = EnsureThreadPool();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<EncodingCompleteEvent> completeEvent =
|
||||
new EncodingCompleteEvent(aEncodeCallback);
|
||||
|
||||
@ -252,7 +247,7 @@ nsresult ImageEncoder::ExtractDataFromLayersImageAsync(
|
||||
new EncodingRunnable(aType, aOptions, nullptr, aImage, encoder,
|
||||
completeEvent, imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
size, aUsePlaceholder, aUsingCustomOptions);
|
||||
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
return NS_DispatchToBackgroundThread(event.forget());
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -265,18 +260,13 @@ nsresult ImageEncoder::ExtractDataAsync(
|
||||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||
}
|
||||
|
||||
nsresult rv = EnsureThreadPool();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<EncodingCompleteEvent> completeEvent =
|
||||
new EncodingCompleteEvent(aEncodeCallback);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(
|
||||
aType, aOptions, std::move(aImageBuffer), nullptr, encoder, completeEvent,
|
||||
aFormat, aSize, aUsePlaceholder, aUsingCustomOptions);
|
||||
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
return NS_DispatchToBackgroundThread(event.forget());
|
||||
}
|
||||
|
||||
/*static*/
|
||||
@ -430,75 +420,5 @@ already_AddRefed<imgIEncoder> ImageEncoder::GetImageEncoder(nsAString& aType) {
|
||||
return encoder.forget();
|
||||
}
|
||||
|
||||
class EncoderThreadPoolTerminator final : public nsIObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Observe(nsISupports*, const char* topic,
|
||||
const char16_t*) override {
|
||||
NS_ASSERTION(!strcmp(topic, "xpcom-shutdown-threads"), "Unexpected topic");
|
||||
if (ImageEncoder::sThreadPool) {
|
||||
ImageEncoder::sThreadPool->Shutdown();
|
||||
ImageEncoder::sThreadPool = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~EncoderThreadPoolTerminator() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(EncoderThreadPoolTerminator, nsIObserver)
|
||||
|
||||
static void RegisterEncoderThreadPoolTerminatorObserver() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
NS_ASSERTION(os, "do_GetService failed");
|
||||
os->AddObserver(new EncoderThreadPoolTerminator(), "xpcom-shutdown-threads",
|
||||
false);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult ImageEncoder::EnsureThreadPool() {
|
||||
if (!sThreadPool) {
|
||||
nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
|
||||
sThreadPool = threadPool;
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"dom::ImageEncoder::EnsureThreadPool",
|
||||
[]() -> void { RegisterEncoderThreadPoolTerminatorObserver(); }));
|
||||
} else {
|
||||
RegisterEncoderThreadPoolTerminatorObserver();
|
||||
}
|
||||
|
||||
const uint32_t kThreadLimit = 2;
|
||||
const uint32_t kIdleThreadLimit = 1;
|
||||
const uint32_t kIdleThreadTimeoutMs = 30000;
|
||||
|
||||
nsresult rv = sThreadPool->SetName(NS_LITERAL_CSTRING("EncodingRunnable"));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = sThreadPool->SetThreadLimit(kThreadLimit);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = sThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = sThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "nsSize.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
class nsIThreadPool;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -95,11 +94,6 @@ class ImageEncoder {
|
||||
// undefined in this case.
|
||||
static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
|
||||
|
||||
static nsresult EnsureThreadPool();
|
||||
|
||||
// Thread pool for dispatching EncodingRunnable.
|
||||
static StaticRefPtr<nsIThreadPool> sThreadPool;
|
||||
|
||||
friend class EncodingRunnable;
|
||||
friend class EncoderThreadPoolTerminator;
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user