68.13 - dom

This commit is contained in:
Fedor 2023-05-31 17:43:30 +03:00
parent f6176dd28b
commit adbe3a4a0f
3233 changed files with 80067 additions and 69353 deletions

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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";

View File

@ -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

View File

@ -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) {

View File

@ -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,

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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 &&

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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;
}

View File

@ -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 = [

View File

@ -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]

View File

@ -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>

View File

@ -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>

View File

@ -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');

View File

@ -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");

View File

@ -60,7 +60,7 @@ const testcases = [
property: "-moz-user-modify"
},
{
property: "-moz-user-select"
property: "user-select"
},
{
property: "-moz-window-dragging"

View File

@ -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>

View File

@ -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 };
}
/**

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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,

View File

@ -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
View 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
View 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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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
View 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

View File

@ -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
View 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

View File

@ -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();

View File

@ -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

View File

@ -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);
}
//----------------------------------------------------------------------

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;
},

View File

@ -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;
}

View File

@ -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"

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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)) {

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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__

View File

@ -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"));

View File

@ -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; }

View File

@ -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];

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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();
;
}

View File

@ -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; }

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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();
}

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
/**

View File

@ -6,7 +6,6 @@
#include "mozilla/AbstractThread.h"
#include "mozilla/SchedulerGroup.h"
#include "nsINamed.h"
using namespace mozilla;
using namespace mozilla::dom;

View File

@ -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) {

View File

@ -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

View File

@ -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");
}

View File

@ -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"

View File

@ -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 objects local name is a valid custom element name, or
* context objects is value is not null, then:
* If definition is not null and definitions 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

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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"

View File

@ -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"

View File

@ -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) {

View File

@ -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);
/**

View File

@ -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;

View File

@ -11,7 +11,6 @@
#include "mozilla/dom/WindowBinding.h"
#include "nsComponentManagerUtils.h"
#include "nsGlobalWindow.h"
#include "nsISupportsPrimitives.h"
#include "nsPIDOMWindow.h"
namespace mozilla {

View File

@ -12,7 +12,6 @@
#include "nsCycleCollectionParticipant.h"
#include "nsDOMNavigationTiming.h"
#include "nsICancelableRunnable.h"
#include "nsIRunnable.h"
#include "nsString.h"
class nsPIDOMWindowInner;

View File

@ -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

View File

@ -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